<commit_before>// General mathematical functions that are used during simulations

// Determines the time between now and an event which occurs on a probabilistic basis
function TimeUntilEvent(Probability){
	// r=annual probability
	// p=random number between 0 and 1
	// p=r^t
	// p=e^(ln(r)t)
	// t=ln(p)/ln(r)
	return (Math.log(Rand.Value())/Math.log(1-Probability));
}

// Event vector
// The event vector allows "Discrete event simulation" as a means to limit the processing power and memory needed, while still being able to extract data in an efficient manner.
function EventVector(){
	this.ValueVec=[];
	this.Time=[];
	this.NumberOfEvents=0;
}

EventVector.prototype.Set= function (InputValue, Time){
	this.DeleteFutureEvents(Time);
	//Add new items to the end of the array
	this.ValueVec.push(InputValue);
	this.Time.push(Time);
	this.NumberOfEvents++;
};

EventVector.prototype.DeleteFutureEvents= function (Time){
	if (this.NumberOfEvents>0){
		//Determine if the current time is prior the last time
		if (Time<=this.Time[this.NumberOfEvents-1]){//falls directly on that time, delete because we don't want zero time events
			//Determine where the current event would sit
			var Current=this.Get(Time);
			if (Current.Time==Time){//falls directly on that time, delete because we don't want zero time events
				PosToDelete=Current.Pos;
			}else{
				PosToDelete=Current.Pos+1;
			}
			NumToDelete=this.NumberOfEvents-PosToDelete;
			//if there dates after this one, delete all future events
			this.ValueVec.splice(PosToDelete, NumToDelete);
			this.Time.splice(PosToDelete, NumToDelete);
			this.NumberOfEvents=PosToDelete;
		}
	}
};

EventVector.prototype.Get= function (Time){
	//Current.Value=Value at this time
	//Current.Time=Time when this value first started
	//Current.Pos=Position in the array that this time occurred
	var Current={};
	if (this.NumberOfEvents==0){// if no times are set
		Current.Value=Number.NaN;
		Current.Time=Number.NaN;
		Current.Pos=Number.NaN;
		return Current;
	}
	else if (Time<this.Time[0]){ // if before the first one is set
		Current.Value=Number.NaN;
		Current.Time=Number.NaN;
		Current.Pos=-1;//note the negative position indicates the position relative to the zeroth index (i.e. this event occurs prior to i=0)
		return Current;
	}
	else if (this.Time[this.NumberOfEvents-1]<=Time){ // if after the last time
		Current.Value=this.ValueVec[this.NumberOfEvents-1];
		Current.Time=this.Time[this.NumberOfEvents-1];
		Current.Pos=this.NumberOfEvents-1;
		return Current;
	}
	else {//loop through all the middle places (the above should have ensured that there are at least 2 items in the array)
		for (var i=0; i<this.NumberOfEvents-1; i++){
			if (this.Time[i]<=Time&&Time<this.Time[i+1]){
				Current.Value=this.ValueVec[i];
				Current.Time=this.Time[i];
				Current.Pos=i;
				return Current;
			}
		}
	}
};

EventVector.prototype.Value= function (Time){

	if (this.NumberOfEvents==0){// if no times are set
		return Number.NaN;
	}
	else if (Time<this.Time[0]){ // if before the first one is set
		return Number.NaN;
	}
	else if (this.Time[this.NumberOfEvents-1]<=Time){ // if after the last time. Checking this first will be much more efficient
		return this.ValueVec[this.NumberOfEvents-1];
	}
	else {//loop through all the middle places (the above should have ensured that there are at least 2 items in the array)
		for (var i=0; i<this.NumberOfEvents-1; i++){
			if (this.Time[i]<=Time&&Time<this.Time[i+1]){
				return this.ValueVec[i];
			}
		}
	}
};

EventVector.prototype.GetRange= function (Time1, Time2, StepSize){
	// This function returns the value of the variable from Time1 to Time2 with time between determined by StepSize
	//Note that this algorithm is pretty inefficient. We'll need to vectorise this

	console.error("this is highly inefficient and should not be used. Instead, convert to an event counting/displying function");

	var ReturnVec=[];
	tCount=0;
	for (var t=Time1; t<Time2; StepSize){
		ReturnVec[tcount]=this.Value(t);
		tCount++;
	}
	ReturnVec[tcount]=this.Value(Time2);// Note that this value is here to avoid rounding error

	return ReturnVec;
};

// (Value, Time1, Time2, StepSize)
// Return a 0 or 1, depending on whether this value is true or not
// ValueByTimeVec=this.GetRange(Time1, Time2, StepSize);
// for each element of ValueByTimeVec
// if the value is the same in that point
// Set that element to true, else false

EventVector.prototype.TimeOf= function (EventValue){
	//Returns a vector of the time of all the events that match that description.
	// for each element of the vector
	// if the event matches
	// add time to the return vector
	var DatesThisEventOccurred=[];
	for (var i=0; i<this.ValueVec.length; i++){
		if (EventValue==this.ValueVec[i]){
			DatesThisEventOccurred.push(this.Time[i]);
		}
	}
	return DatesThisEventOccurred;
};

EventVector.prototype.FirstTimeOf= function (EventValue){
	for (var i=0; i<this.ValueVec.length; i++){
		if (EventValue==this.ValueVec[i]){
			return this.Time[i];
		}
	}
	return NaN;
};

EventVector.prototype.Next= function (Time){
	var Current={};
	if (this.NumberOfEvents==0){// if no times are set
		Current.Value=Number.NaN;
		Current.Time=Number.NaN;
		Current.Pos=Number.NaN;
		return Current;
	}
	else if (this.Time[this.NumberOfEvents-1]<=Time){ // if after the last time
		// Note that this is non-inclusive
		Current.Value=NaN;
		Current.Time=NaN;
		Current.Pos=NaN;// indicates thar there are no future events
		return Current;
	}
	else {//loop through all the middle places (the above should have ensured that there are at least 2 items in the array)
		for (var i=0; i<this.NumberOfEvents && Time<this.Time[i]; i++){// this is a rare example of the double for loop condition. Care needs to be taken that the period is inclusive. That is, i++ is different to ++i
			if (Time<this.Time[i]){
				Current.Value=this.ValueVec[i];
				Current.Time=this.Time[i];
				Current.Pos=i;
				return Current;
			}
		}
	}

	// if an element is not fount, return a NaN result
	Current.Value=Number.NaN;
	Current.Time=Number.NaN;
	Current.Pos=Number.NaN;
	return Current;
};

EventVector.prototype.EventsBetween= function (Time1, Time2){
	// Note that this function only returns values between
	// It is left side inclusive, so if there is an event '22' at 2010, b=a.EventsBetween(2010, 2011); gives b[0].Value=22
	// It does not count events priot to Time1, so if there is an event '22' at 2010 and so if there is an event '11' at 1999, b=a.EventsBetween(2010, 2011); gives b[0].Value=22, not 11

	if (Time1>Time2){
		console.error("Error trace");
		throw "An error occured in determining events. See error trace above";
	}

	var Current=[];
	var EventCount=-1;
	if (this.NumberOfEvents==0){// if no times are set
		return Current;
	}
	else if (this.Time[this.NumberOfEvents-1]<Time1){ // if after the start time
		return Current;
	}
	else {//loop through all the middle places (the above should have ensured that there are at least 2 items in the array)
		console.log("Got to the point");
		for (var i=0; i<this.NumberOfEvents && this.Time[i]<Time2; i++){// this is a rare example of the double for loop condition. Care needs to be taken that the period is inclusive. That is, i++ is different to ++i
			if (Time1<=this.Time[i] && this.Time[i]<Time2){
				EventCount++;
				Current[EventCount]={};

				Current[EventCount].Value=this.ValueVec[i];
				Current[EventCount].Time=this.Time[i];
				Current[EventCount].Pos=i;
			}
		}
	}

	// if an element is not fount, return an empty vector
	return Current;
};

//a=new EventVector();
//a.Set(0, 1985);
//a.Set(10, 1990);
//a.Set(20, 1997);
//a.Set(30, 2003);
//a.Set(40, 2007);
//a.Set(50, 2010);

EventVector.prototype.CountEvents= function (EventValue, Time1, Time2){
	// Counts the number of times the event happens in the period [Year1, Year2)
	console.error("This function is not yet implemented.");

};

//DurationOf(EventValue, WhenToStop(i.e. death))
// Determines the total amount of time spent in this status

<commit_msg>Fixed .Next function<commit_after>// General mathematical functions that are used during simulations

// Determines the time between now and an event which occurs on a probabilistic basis
function TimeUntilEvent(Probability){
	// r=annual probability
	// p=random number between 0 and 1
	// p=r^t
	// p=e^(ln(r)t)
	// t=ln(p)/ln(r)
	return (Math.log(Rand.Value())/Math.log(1-Probability));
}

// Event vector
// The event vector allows "Discrete event simulation" as a means to limit the processing power and memory needed, while still being able to extract data in an efficient manner.
function EventVector(){
	this.ValueVec=[];
	this.Time=[];
	this.NumberOfEvents=0;
}

EventVector.prototype.Set= function (InputValue, Time){
	this.DeleteFutureEvents(Time);
	//Add new items to the end of the array
	this.ValueVec.push(InputValue);
	this.Time.push(Time);
	this.NumberOfEvents++;
};

EventVector.prototype.DeleteFutureEvents= function (Time){
	if (this.NumberOfEvents>0){
		//Determine if the current time is prior the last time
		if (Time<=this.Time[this.NumberOfEvents-1]){//falls directly on that time, delete because we don't want zero time events
			//Determine where the current event would sit
			var Current=this.Get(Time);
			if (Current.Time==Time){//falls directly on that time, delete because we don't want zero time events
				PosToDelete=Current.Pos;
			}else{
				PosToDelete=Current.Pos+1;
			}
			NumToDelete=this.NumberOfEvents-PosToDelete;
			//if there dates after this one, delete all future events
			this.ValueVec.splice(PosToDelete, NumToDelete);
			this.Time.splice(PosToDelete, NumToDelete);
			this.NumberOfEvents=PosToDelete;
		}
	}
};

EventVector.prototype.Get= function (Time){
	//Current.Value=Value at this time
	//Current.Time=Time when this value first started
	//Current.Pos=Position in the array that this time occurred
	var Current={};
	if (this.NumberOfEvents==0){// if no times are set
		Current.Value=Number.NaN;
		Current.Time=Number.NaN;
		Current.Pos=Number.NaN;
		return Current;
	}
	else if (Time<this.Time[0]){ // if before the first one is set
		Current.Value=Number.NaN;
		Current.Time=Number.NaN;
		Current.Pos=-1;//note the negative position indicates the position relative to the zeroth index (i.e. this event occurs prior to i=0)
		return Current;
	}
	else if (this.Time[this.NumberOfEvents-1]<=Time){ // if after the last time
		Current.Value=this.ValueVec[this.NumberOfEvents-1];
		Current.Time=this.Time[this.NumberOfEvents-1];
		Current.Pos=this.NumberOfEvents-1;
		return Current;
	}
	else {//loop through all the middle places (the above should have ensured that there are at least 2 items in the array)
		for (var i=0; i<this.NumberOfEvents-1; i++){
			if (this.Time[i]<=Time&&Time<this.Time[i+1]){
				Current.Value=this.ValueVec[i];
				Current.Time=this.Time[i];
				Current.Pos=i;
				return Current;
			}
		}
	}
};

EventVector.prototype.Value= function (Time){

	if (this.NumberOfEvents==0){// if no times are set
		return Number.NaN;
	}
	else if (Time<this.Time[0]){ // if before the first one is set
		return Number.NaN;
	}
	else if (this.Time[this.NumberOfEvents-1]<=Time){ // if after the last time. Checking this first will be much more efficient
		return this.ValueVec[this.NumberOfEvents-1];
	}
	else {//loop through all the middle places (the above should have ensured that there are at least 2 items in the array)
		for (var i=0; i<this.NumberOfEvents-1; i++){
			if (this.Time[i]<=Time&&Time<this.Time[i+1]){
				return this.ValueVec[i];
			}
		}
	}
};

EventVector.prototype.GetRange= function (Time1, Time2, StepSize){
	// This function returns the value of the variable from Time1 to Time2 with time between determined by StepSize
	//Note that this algorithm is pretty inefficient. We'll need to vectorise this

	console.error("this is highly inefficient and should not be used. Instead, convert to an event counting/displying function");

	var ReturnVec=[];
	tCount=0;
	for (var t=Time1; t<Time2; StepSize){
		ReturnVec[tcount]=this.Value(t);
		tCount++;
	}
	ReturnVec[tcount]=this.Value(Time2);// Note that this value is here to avoid rounding error

	return ReturnVec;
};

// (Value, Time1, Time2, StepSize)
// Return a 0 or 1, depending on whether this value is true or not
// ValueByTimeVec=this.GetRange(Time1, Time2, StepSize);
// for each element of ValueByTimeVec
// if the value is the same in that point
// Set that element to true, else false

EventVector.prototype.TimeOf= function (EventValue){
	//Returns a vector of the time of all the events that match that description.
	// for each element of the vector
	// if the event matches
	// add time to the return vector
	var DatesThisEventOccurred=[];
	for (var i=0; i<this.ValueVec.length; i++){
		if (EventValue==this.ValueVec[i]){
			DatesThisEventOccurred.push(this.Time[i]);
		}
	}
	return DatesThisEventOccurred;
};

EventVector.prototype.FirstTimeOf= function (EventValue){
	for (var i=0; i<this.ValueVec.length; i++){
		if (EventValue==this.ValueVec[i]){
			return this.Time[i];
		}
	}
	return NaN;
};

EventVector.prototype.Next= function (Time){
	var Current={};
	if (this.NumberOfEvents==0){// if no times are set
		Current.Value=Number.NaN;
		Current.Time=Number.NaN;
		Current.Pos=Number.NaN;
		return Current;
	}
	else if (this.Time[this.NumberOfEvents-1]<=Time){ // if after the last time
		// Note that this is non-inclusive
		Current.Value=NaN;
		Current.Time=NaN;
		Current.Pos=NaN;// indicates thar there are no future events
		return Current;
	}
	else {//loop through all the middle places (the above should have ensured that there are at least 2 items in the array)
		for (var i=0; i<this.NumberOfEvents; i++){
			if (Time<this.Time[i]){
				Current.Value=this.ValueVec[i];
				Current.Time=this.Time[i];
				Current.Pos=i;
				return Current;
			}
		}
	}

	// if an element is not fount, return a NaN result
	Current.Value=Number.NaN;
	Current.Time=Number.NaN;
	Current.Pos=Number.NaN;
	return Current;
};

// a=new EventVector();
// a.Set(0, 1985);
// a.Set(10, 1990);
// a.Set(20, 1997);
// a.Set(30, 2003);
// a.Set(40, 2007);
// a.Set(50, 2010);
// b=a.Next(1)
// b=a.Next(1980)
// b=a.Next(1985)
// b=a.Next(2009)
// b=a.Next(2010)

EventVector.prototype.EventsBetween= function (Time1, Time2){
	// Note that this function only returns values between
	// It is left side inclusive, so if there is an event '22' at 2010, b=a.EventsBetween(2010, 2011); gives b[0].Value=22
	// It does not count events priot to Time1, so if there is an event '22' at 2010 and so if there is an event '11' at 1999, b=a.EventsBetween(2010, 2011); gives b[0].Value=22, not 11

	if (Time1>Time2){
		console.error("Error trace");
		throw "An error occured in determining events. See error trace above";
	}

	var Current=[];
	var EventCount=-1;
	if (this.NumberOfEvents==0){// if no times are set
		return Current;
	}
	else if (this.Time[this.NumberOfEvents-1]<Time1){ // if after the start time
		return Current;
	}
	else {//loop through all the middle places (the above should have ensured that there are at least 2 items in the array)
		console.log("Got to the point");
		for (var i=0; i<this.NumberOfEvents && this.Time[i]<Time2; i++){// this is a rare example of the double for loop condition. Care needs to be taken that the period is inclusive. That is, i++ is different to ++i
			if (Time1<=this.Time[i] && this.Time[i]<Time2){
				EventCount++;
				Current[EventCount]={};

				Current[EventCount].Value=this.ValueVec[i];
				Current[EventCount].Time=this.Time[i];
				Current[EventCount].Pos=i;
			}
		}
	}

	// if an element is not fount, return an empty vector
	return Current;
};

//a=new EventVector();
//a.Set(0, 1985);
//a.Set(10, 1990);
//a.Set(20, 1997);
//a.Set(30, 2003);
//a.Set(40, 2007);
//a.Set(50, 2010);
// b=a.EventsBetween(1990, 1986);
// b=a.EventsBetween(1970, 1986);
// b=a.EventsBetween(1970, 1971);
// b=a.EventsBetween(1970, 2011);
// b=a.EventsBetween(2010, 2011);
// b=a.EventsBetween(2011, 2012);

EventVector.prototype.CountEvents= function (EventValue, Time1, Time2){
	// Counts the number of times the event happens in the period [Year1, Year2)
	console.error("This function is not yet implemented.");

};

//DurationOf(EventValue, WhenToStop(i.e. death))
// Determines the total amount of time spent in this status

<|endoftext|>


<commit_before>var sys = require ("sys")
var functional = require ("./functional")

for (fName in functional) if (functional.hasOwnProperty (fName))
    global[fName] = functional[fName]

var tokenize = function (string) {
    var tokens = []
    var parseStack = [tokens]
    var specials = []

    var acceptToken = function (t) { parseStack [0].push (t) }

    var increaseNesting = function (special) {
	var newArr = []
	acceptToken (newArr)
	parseStack.unshift (newArr)
	specials.unshift (special || false)
    }

    var decreaseNesting = function () {
	parseStack.shift ()
	if (specials.shift ()) decreaseNesting ()
	if (parseStack.length === 0)
	    throw new Error (
		"unbalanced parens:\n" +
		    sys.inspect (parseStack))
    }

    var handleSpecial = function (special) {
	if (special === "'") {
	    increaseNesting (true)
	    acceptToken ("quote")
	}
    }

    var handleToken = function (token) {
	var token = token
	var special

	if (token.match (/^[`',]/)) {
	    handleSpecial (token [0])
	    token = token.slice (1)
	}

	if (token === '(') increaseNesting ()
	else if (token === ')') decreaseNesting ()
	else if (token.match (/^-?[0-9.]+$/))
	    acceptToken (parseFloat (token))
	else acceptToken (token)
    }

    string
	.replace (/([^\\])\n/, '$1 ')
	.replace (/\s+\\\n/, "\\n")
	.match (/(;.*)|("([^"]|(\\"))*?[^\\]")|[&']?[*.a-z-]+|[><=!\+\/\*-]+|-?[0-9.]+|([']?\()|\)/g)
	.forEach (handleToken)

    if (parseStack.length > 1) {
	sys.puts (sys.inspect (parseStack[0]))
	throw new Error ("unexpected EOF, probably missing a )")
    }

    return tokens
}

var reprint = function (tokens, nesting) {
    var nesting = nesting || 0
    for (var i=0; i<nesting - 1; i++) sys.print ("\t")
    if (nesting > 0) sys.print ("(")

    tokens.forEach (function (item) {
	if (item.constructor.name === 'Array') {
	    sys.print ("\n")
	    reprint (item, nesting + 1)
	} else {
	    sys.print ("<" + item + ">")
	}
    })
    if (nesting > 0) sys.print (")")
}
var indent = function () {
    return compact (Array.prototype.slice.call (arguments))
	.join("\n")
	.replace (/^/, "\n")
	.replace (/\n/g, "\n  ") + "\n"
}

var constructHash = function (arrayOfArrays) {
    return inject ({}, arrayOfArrays, function (object, item) {
	object [item [0]] = object [item [1]]
	return object
    })
}

var macros = {}

macros['return'] = function (token) {
    if (token && token.constructor.name === "Array" && token[0] === "return")
	return translate (token)
    return "return " + translate (token)
}

macros.let = function (assignments, body) {
    var body = Array.prototype.slice.call (arguments, 1)
    body [body.length - 1] = ['return', body [body.length - 1]]

    var content = indent (
	"var " + map (assignments, function (kv) {
	    return kv [0] + " = " + translate (kv [1])
	}).join(",\n  ") + ";",
	map (body, function (arg) {
	    return translate (arg) + ';'
	}).join("\n")
    )

    return "(function() {" + content + "})();\n"
}

macros.setq = function (name, value) {
    return "var " + translate (name) + " = " + translate (value) + ";\n"
}

macros.statement = function () {
    return macros.call.apply (null, arguments) + ";\n"
}

macros.progn = function (body) {
    var body = Array.prototype.slice.call (arguments)
    body [body.length - 1] = ['return', body [body.length - 1]]

    return map (body, function (arg) {
	return translate (arg) + ';'
    }).join("\n")
}

macros.call = function (fnName) {
    return translate (fnName) + "(" +
	map (
	    Array.prototype.slice.call (arguments, 1),
	    translate
	).join (", ") + ")"
}

macros.defun = function (fnName, arglist, body) {
    return translate (fnName) + " = " +
	macros.lambda.apply (
	    undefined,
	    Array.prototype.slice.call (arguments, 1)
	) + ";\n"
}

macros.macroexpand = function (name) {
    if (macros [name])
	return macros[name].toString ()
    else return "undefined"
}

macros.defmacro = function (name, arglist, body) {
    macros[name] = eval (macros.lambda (arglist, body))
}

var joinWith = function (string) {
    var string = string
    return function () {
	return map (
	    Array.prototype.slice.apply (arguments),
	    translate
	).join (" " + string + " ")
    }
}

macros['+'] = joinWith ('+')
macros['concat'] = joinWith ('+')
macros['-'] = joinWith ('-')
macros['*'] = joinWith ('*')
macros['/'] = joinWith ('*')

var transformArgs = function (arglist) {
    var last, args = []
    arglist.forEach (function (arg) {
	if (arg [0] === '&') last = arg.slice (1)
	else {
	    args.push ([last || 'required', arg])
	    last = null
	}
    })

    if (last) throw new Error ("unexpected argument modifier: " + last)

    return args
}

var reverse = macros.reverse = function (arr) {
    var reversed = []
    arr.forEach (function (item) { reversed.unshift (item) })
    return reversed
}

var buildArgsString = function (args, rest) {
    var argsString = "", optionalCount = 0
    args.forEach (function (arg, optionIndex) {
	if (arg [0] === 'optional') {
	    argsString += "if (arguments.length < " + (args.length - optionalCount) + ") " +
		"// if " + arg[1] + " is missing" +
		indent ("var "+
			map (args.slice (optionIndex + 1), function (arg, argIndex) {
			    return arg[1] + " = " + args [optionIndex + argIndex][1]
			}).reverse().join(", ") +
			", " + arg [1] + " = undefined;"
		)
	    optionalCount ++
	}
    })

    var argumentCountMismatch = function (msg) {
	return indent ('throw new Error("argument count mismatch: ' + msg + '");')
    }

    if (rest) {
	argsString += indent ("var " + translate (rest [1]) +
			      " = Array.prototype.slice.call(arguments, " +
			      (noRestArgs.length) + ");")

	if (args.length - optionalCount > 0)
	    argsString += "if (arguments.length < " + (args.length - optionalCount) + ")" +
	    argumentCountMismatch ('expected no fewer than ' +
				   (args.length - optionalCount) + ' arguments')
    } else {
	if (args.length === 0)
	    argsString += 'if (arguments.length > 0)' +
	    argumentCountMismatch ('expected no arguments')

	else if (optionalCount > 0)
	    argsString += 'if (arguments.length < ' + (args.length - optionalCount) +
		    ' || arguments.length > ' + args.length + ')' +
		    argumentCountMismatch ('expected between ' +
					   (args.length - optionalCount) +
					   ' and ' + args.length + ' arguments");')
    }

    return argsString
}

var buildCommentString = function (args) {
    if (args.length === 0) return ""
    return "// " + map (args, function (arg) {return reverse(arg).join(":")}).join(" ")
}

macros.lambda = function (arglist, body) {
    var args = transformArgs (arglist)
    var rest = detect (args, function (arg) {return arg [0] === 'rest' })

    var noRestArgs = rest ? args.slice (0, -1) : args
    var argsString = buildArgsString (noRestArgs, rest)
    var commentString = buildCommentString (args)

    return "(function(" +
	map (args, function (arg) {return translate (arg [1])}).join (", ") +
	") { "+indent (
		argsString +
		translate (macros['return'] (body))
	) + "})"
}

macros.map = map

macros.quote = function (item) {
    if (item.constructor.name === "Array")
	return '[' + map (item, macros.quote).join(", ") + ']'
    else if (typeof item === 'number')
	return item
    else return '"' + literal (item) + '"'
}

var literal = function (string) {
    return inject (
	string.replace (/\*/g, '_'),
	string.match (/-(.)/g),
	function (returnString, match) {
	    return returnString.replace (
		match,
		match[1].toUpperCase ()
	    )
	}
    )
}

var translate = function (token, hint) {
    var hint = hint
    if (hint && typeof macros [hint] === 'undefined')
	hint = undefined

    try {
	if (token.constructor.name === 'Array') {
	    if ('undefined' === typeof macros [token [0]])
		return macros [hint || 'call']
		.apply (null, token)
	    else return macros [token [0]]
		.apply (null, token.slice (1))
	} else if (typeof token === 'string' && token.match (/^[*a-z-]+$/))
	    return literal (token)
	else if (typeof token === 'string' && token.match (/^;/))
	    return token.replace (/^;+/, '//')
	else return token
    } catch (e) {
	sys.puts (e.stack)
	sys.print ("Encountered when attempting to process:")
	sys.puts (indent (sys.inspect (token)))
    }
}

var fs = require ("fs")

tokenize (fs.readFileSync ("macros.lisp", 'utf8')).forEach (translate)

process.argv.slice(2).forEach (function (file) {
    tokenize (fs.readFileSync (file, 'utf8'))
	.forEach (function (token) {
	    var line = translate (token, 'statement')
	    if (line) sys.puts (line)
	})
})

<commit_msg>[ better error reporting on failed macros ]<commit_after>var sys = require ("sys")
var functional = require ("./functional")

for (fName in functional) if (functional.hasOwnProperty (fName))
    global[fName] = functional[fName]

var tokenize = function (string) {
    var tokens = []
    var parseStack = [tokens]
    var specials = []

    var acceptToken = function (t) { parseStack [0].push (t) }

    var increaseNesting = function (special) {
	var newArr = []
	acceptToken (newArr)
	parseStack.unshift (newArr)
	specials.unshift (special || false)
    }

    var decreaseNesting = function () {
	parseStack.shift ()
	if (specials.shift ()) decreaseNesting ()
	if (parseStack.length === 0)
	    throw new Error (
		"unbalanced parens:\n" +
		    sys.inspect (parseStack))
    }

    var handleSpecial = function (special) {
	if (special === "'") {
	    increaseNesting (true)
	    acceptToken ("quote")
	}
    }

    var handleToken = function (token) {
	var token = token
	var special

	if (token.match (/^[`',]/)) {
	    handleSpecial (token [0])
	    token = token.slice (1)
	}

	if (token === '(') increaseNesting ()
	else if (token === ')') decreaseNesting ()
	else if (token.match (/^-?[0-9.]+$/))
	    acceptToken (parseFloat (token))
	else acceptToken (token)
    }

    string
	.replace (/([^\\])\n/, '$1 ')
	.replace (/\s+\\\n/, "\\n")
	.match (/(;.*)|("([^"]|(\\"))*?[^\\]")|[&']?[*.a-z-]+|[><=!\+\/\*-]+|-?[0-9.]+|([']?\()|\)/g)
	.forEach (handleToken)

    if (parseStack.length > 1) {
	sys.puts (sys.inspect (parseStack[0]))
	throw new Error ("unexpected EOF, probably missing a )")
    }

    return tokens
}

var reprint = function (tokens, nesting) {
    var nesting = nesting || 0
    for (var i=0; i<nesting - 1; i++) sys.print ("\t")
    if (nesting > 0) sys.print ("(")

    tokens.forEach (function (item) {
	if (item.constructor.name === 'Array') {
	    sys.print ("\n")
	    reprint (item, nesting + 1)
	} else {
	    sys.print ("<" + item + ">")
	}
    })
    if (nesting > 0) sys.print (")")
}
var indent = function () {
    return compact (Array.prototype.slice.call (arguments))
	.join("\n")
	.replace (/^/, "\n")
	.replace (/\n/g, "\n  ") + "\n"
}

var constructHash = function (arrayOfArrays) {
    return inject ({}, arrayOfArrays, function (object, item) {
	object [item [0]] = object [item [1]]
	return object
    })
}

var macros = {}

macros['return'] = function (token) {
    if (token && token.constructor.name === "Array" && token[0] === "return")
	return translate (token)
    return "return " + translate (token)
}

macros.let = function (assignments, body) {
    var body = Array.prototype.slice.call (arguments, 1)
    body [body.length - 1] = ['return', body [body.length - 1]]

    var content = indent (
	"var " + map (assignments, function (kv) {
	    return kv [0] + " = " + translate (kv [1])
	}).join(",\n  ") + ";",
	map (body, function (arg) {
	    return translate (arg) + ';'
	}).join("\n")
    )

    return "(function() {" + content + "})();\n"
}

macros.setq = function (name, value) {
    return "var " + translate (name) + " = " + translate (value) + ";\n"
}

macros.statement = function () {
    return macros.call.apply (null, arguments) + ";\n"
}

macros.progn = function (body) {
    var body = Array.prototype.slice.call (arguments)
    body [body.length - 1] = ['return', body [body.length - 1]]

    return map (body, function (arg) {
	return translate (arg) + ';'
    }).join("\n")
}

macros.call = function (fnName) {
    return translate (fnName) + "(" +
	map (
	    Array.prototype.slice.call (arguments, 1),
	    translate
	).join (", ") + ")"
}

macros.defun = function (fnName, arglist, body) {
    return translate (fnName) + " = " +
	macros.lambda.apply (
	    undefined,
	    Array.prototype.slice.call (arguments, 1)
	) + ";\n"
}

macros.macroexpand = function (name) {
    if (macros [name])
	return macros[name].toString ()
    else return "undefined"
}

macros.defmacro = function (name, arglist, body) {
    try {
	macros[name] = eval (macros.lambda.apply (
	    undefined,
	    Array.prototype.slice.call (arguments, 1)
	))
    } catch (e) {
	sys.puts ("error in parsing macro " + name + ":")
	sys.puts (indent (macros.lambda (arglist, body)))
	throw e
    }
}

var joinWith = function (string) {
    var string = string
    return function () {
	return map (
	    Array.prototype.slice.apply (arguments),
	    translate
	).join (" " + string + " ")
    }
}

macros['+'] = joinWith ('+')
macros['concat'] = joinWith ('+')
macros['-'] = joinWith ('-')
macros['*'] = joinWith ('*')
macros['/'] = joinWith ('*')

var transformArgs = function (arglist) {
    var last, args = []
    arglist.forEach (function (arg) {
	if (arg [0] === '&') last = arg.slice (1)
	else {
	    args.push ([last || 'required', arg])
	    last = null
	}
    })

    if (last) throw new Error ("unexpected argument modifier: " + last)

    return args
}

var reverse = macros.reverse = function (arr) {
    var reversed = []
    arr.forEach (function (item) { reversed.unshift (item) })
    return reversed
}

var buildArgsString = function (args, rest) {
    var argsString = "", optionalCount = 0
    args.forEach (function (arg, optionIndex) {
	if (arg [0] === 'optional') {
	    argsString += "if (arguments.length < " + (args.length - optionalCount) + ") " +
		"// if " + arg[1] + " is missing" +
		indent ("var "+
			map (args.slice (optionIndex + 1), function (arg, argIndex) {
			    return arg[1] + " = " + args [optionIndex + argIndex][1]
			}).reverse().join(", ") +
			", " + arg [1] + " = undefined;"
		)
	    optionalCount ++
	}
    })

    var argumentCountMismatch = function (msg) {
	return indent ('throw new Error("argument count mismatch: ' + msg + '");')
    }

    if (rest) {
	argsString += indent ("var " + translate (rest [1]) +
			      " = Array.prototype.slice.call(arguments, " +
			      (noRestArgs.length) + ");")

	if (args.length - optionalCount > 0)
	    argsString += "if (arguments.length < " + (args.length - optionalCount) + ")" +
	    argumentCountMismatch ('expected no fewer than ' +
				   (args.length - optionalCount) + ' arguments')
    } else {
	if (args.length === 0)
	    argsString += 'if (arguments.length > 0)' +
	    argumentCountMismatch ('expected no arguments')

	else if (optionalCount > 0)
	    argsString += 'if (arguments.length < ' + (args.length - optionalCount) +
		    ' || arguments.length > ' + args.length + ')' +
		    argumentCountMismatch ('expected between ' +
					   (args.length - optionalCount) +
					   ' and ' + args.length + ' arguments");')
    }

    return argsString
}

var buildCommentString = function (args) {
    if (args.length === 0) return ""
    return "// " + map (args, function (arg) {return reverse(arg).join(":")}).join(" ")
}

macros.lambda = function (arglist, body) {
    var args = transformArgs (arglist)
    var rest = detect (args, function (arg) {return arg [0] === 'rest' })

    var noRestArgs = rest ? args.slice (0, -1) : args
    var argsString = buildArgsString (noRestArgs, rest)
    var commentString = buildCommentString (args)

    return "(function(" +
	map (args, function (arg) {return translate (arg [1])}).join (", ") +
	") { "+indent (
		argsString +
		translate (macros['return'] (body))
	) + "})"
}

macros.map = map

macros.quote = function (item) {
    if (item.constructor.name === "Array")
	return '[' + map (item, macros.quote).join(", ") + ']'
    else if (typeof item === 'number')
	return item
    else return '"' + literal (item) + '"'
}

var literal = function (string) {
    return inject (
	string.replace (/\*/g, '_'),
	string.match (/-(.)/g),
	function (returnString, match) {
	    return returnString.replace (
		match,
		match[1].toUpperCase ()
	    )
	}
    )
}

var translate = function (token, hint) {
    var hint = hint
    if (hint && typeof macros [hint] === 'undefined')
	hint = undefined

    try {
	if (token.constructor.name === 'Array') {
	    if ('undefined' === typeof macros [token [0]])
		return macros [hint || 'call']
		.apply (null, token)
	    else return macros [token [0]]
		.apply (null, token.slice (1))
	} else if (typeof token === 'string' && token.match (/^[*a-z-]+$/))
	    return literal (token)
	else if (typeof token === 'string' && token.match (/^;/))
	    return token.replace (/^;+/, '//')
	else return token
    } catch (e) {
	sys.puts (e.stack)
	sys.print ("Encountered when attempting to process:")
	sys.puts (indent (sys.inspect (token)))
    }
}

var fs = require ("fs")

tokenize (fs.readFileSync ("macros.lisp", 'utf8')).forEach (translate)

process.argv.slice(2).forEach (function (file) {
    tokenize (fs.readFileSync (file, 'utf8'))
	.forEach (function (token) {
	    var line = translate (token, 'statement')
	    if (line) sys.puts (line)
	})
})

<|endoftext|>


<commit_before>var TimeDegrees = function(){}

TimeDegrees.prototype.clock_degree(timeStr){

}

module.exports = TimeDegrees
<commit_msg>Update time-degrees function<commit_after>var TimeDegrees = function(){}

TimeDegrees.prototype.clock_degree= function(timeStr){

}

module.exports = TimeDegrees
<|endoftext|>


<commit_before>/*
    Copyright 2017 Ahnaf Siddiqui

    Licensed under the Apache License, Version 2.0 (the "License");
    you may not use this file except in compliance with the License.
    You may obtain a copy of the License at

        http://www.apache.org/licenses/LICENSE-2.0

    Unless required by applicable law or agreed to in writing, software
    distributed under the License is distributed on an "AS IS" BASIS,
    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    See the License for the specific language governing permissions and
    limitations under the License.
*/

const ffi = require('ffi');
const ref = require('ref');

// This is the bridge to native Diamond functions.
const platformdir = process.platform;
const intPtr = ref.refType('int');
const Diamond = ffi.Library(__dirname + '/src/CDiamond/lib/' + platformdir + '/libCDiamond', {
// const Diamond = ffi.Library(__dirname + '/src/CDiamond/lib/' + platformdir + '/CDiamond', {
    // Engine2D
    'dEngine2DConfigureGraphics': ['void', ['string', 'int', 'int', 'bool', 'bool']],
    'dEngine2DConfigureAudio': ['void', ['int', 'int', 'int']],
    'dEngine2DInit': ['bool', []],
    'dEngine2DDestroy': ['void', []],
    'dEngine2DLaunchGame': ['void', []],
    // Game2D
    'dGame2DBenchmark': ['void', ['string']],
    'dGame2DInit': ['bool', ['pointer', 'pointer', 'pointer', 'pointer']],
    'dGame2DDestroy': ['void', []],
    // Transform2
    'dTransform2Init': ['bool', []],
    'dTransform2Destroy': ['void', []],
    'dTransform2MakeTransform': ['int', ['float', 'float', 'float', 'float', 'float']],
    'dTransform2DestroyTransform': ['void', ['int']],
    'dTransform2SetTransform': ['void', ['int', 'float', 'float', 'float', 'float', 'float']],
    'dTransform2SetPosition': ['void', ['int', 'float', 'float']],
    'dTransform2GetPositionX': ['float', ['int']],
    'dTransform2GetPositionY': ['float', ['int']],
    'dTransform2AddPosition': ['void', ['int', 'float', 'float']],
    'dTransform2AddPositionX': ['void', ['int', 'float']],
    'dTransform2AddPositionY': ['void', ['int', 'float']],
    'dTransform2GetRotation': ['float', ['int']],
    'dTransform2SetRotation': ['void', ['int', 'float']],
    'dTransform2AddRotation': ['void', ['int', 'float']],
    'dTransform2SetScale': ['void', ['int', 'float', 'float']],
    'dTransform2GetScaleX': ['float', ['int']],
    'dTransform2GetScaleY': ['float', ['int']],
    // Renderer2D
    'dRenderer2DInit': ['bool', []],
    'dRenderer2DDestroy': ['void', []],
    'dRenderer2DGetResolution': ['void', [intPtr, intPtr]],
    'dRenderer2DGetScreenResolution': ['void', [intPtr, intPtr]],
    'dRenderer2DLoadTexture': ['int', ['string']],
    'dRenderer2DDestroyTexture': ['void', ['int']],
    'dRenderer2DMakeRenderComponent': ['int', ['int', 'int', 'int']],
    'dRenderer2DDestroyRenderComponent': ['void', ['int']],
    'dRenderComponent2DSetSprite': ['void', ['int', 'int']],
    'dRenderComponent2DGetLayer': ['int', ['int']],
    'dRenderComponent2DSetLayer': ['void', ['int', 'int']],
    'dRenderComponent2DGetPivotX': ['float', ['int']],
    'dRenderComponent2DGetPivotY': ['float', ['int']],
    'dRenderComponent2DSetPivot': ['void', ['int', 'float', 'float']],
    'dRenderComponent2DFlipX': ['void', ['int']],
    'dRenderComponent2DFlipY': ['void', ['int']],
    'dRenderComponent2DIsFlippedX': ['bool', ['int']],
    'dRenderComponent2DIsFlippedY': ['bool', ['int']],
    // ParticleSystem2D
    'dParticleSystem2DInit': ['bool', ['int']],
    'dParticleSystem2DDestroy': ['void', []],
    'dParticleSystem2DMakeEmitter': ['int', ['int', 'int']],
    'dParticleSystem2DDestroyEmitter': ['void', ['int']],
    'dParticleSystem2DUpdate': ['void', ['int']],
    // Config
    'dConfigInitConfigLoader': ['void', ['string']],
    'dConfigDestroyAll': ['void', []],
    'dConfigMakeConfigTable': ['int', []],
    'dConfigLoadConfigTable': ['int', ['string']],
    'dConfigDestroyConfigTable': ['void', ['int']],
    'dConfigWriteConfig': ['void', ['int', 'string']],
    'dConfigHasKey': ['bool', ['int', 'string']],
    'dConfigGet': ['string', ['int', 'string']],
    'dConfigGetInt': ['int', ['int', 'string']],
    'dConfigGetFloat': ['float', ['int', 'string']],
    'dConfigGetBool': ['bool', ['int', 'string']],
    'dConfigSet': ['void', ['int', 'string', 'string']],
    'dConfigSetInt': ['void', ['int', 'string', 'int']],
    'dConfigSetFloat': ['void', ['int', 'string', 'float']],
    'dConfigSetBool': ['void', ['int', 'string', 'bool']]
});

// API starts here

/**
 * Configuration for Diamond engine with default values.
 */
exports.Config = function() {
    this.windowTitle = "A Game Without a Name";
    this.windowWidth = 1280;
    this.windowHeight = 720;
    this.fullscreen = false;
    this.vsync = false;
    this.numAudioChannels = 2;
    this.audioFrequency = 44100; // hertz
    this.audioSampleSize = 2048; // bytes
    // estimate for the max number of particles
    // that may be present at any time.
    this.particlePoolSize = 100;
    this.benchmark = false;
    this.benchmarkFile = "benchmark.log";
}

/**
 * Initializes Diamond engine and its subsystems.
 */
exports.init = function(config) {
    var success = true;

    if (!config) {
        config = new exports.Config();
    }

    Diamond.dEngine2DConfigureGraphics(
        config.windowTitle,
        config.windowWidth,
        config.windowHeight,
        config.fullscreen,
        config.vsync
    );
    Diamond.dEngine2DConfigureAudio(
        config.numAudioChannels,
        config.audioFrequency,
        config.audioSampleSize
    );

    if (config.benchmark)
        Diamond.dGame2DBenchmark(config.benchmarkFile);

    if (!(Diamond.dEngine2DInit() &&
          Diamond.dTransform2Init() &&
          Diamond.dRenderer2DInit() &&
          Diamond.dParticleSystem2DInit(config.particlePoolSize))) {
        success = false;
    }

    Diamond.dConfigInitConfigLoader("");

    return success;
}

/**
 * Begins the game loop in the Diamond backend.
 */
exports.launch = function(update, postPhysicsUpdate, quit) {
    var postPhysicsUpdateCB = ref.NULL;
    var quitCB = ref.NULL;

    const updateCB = ffi.Callback('void', ['int'], function(delta) {
        if (update) {
            update(delta);
        }

        // update Diamond's non-core systems (ex. particles)
        // that aren't automatically updated within
        // Diamond Engine's game loop.
        Diamond.dParticleSystem2DUpdate(delta);
    });

    if (postPhysicsUpdate) {
        postPhysicsUpdateCB = ffi.Callback('void', ['int'], postPhysicsUpdate);
    }
    if (quit) {
        quitCB = ffi.Callback('void', [], quit);
    }

    Diamond.dGame2DInit(ref.NULL, updateCB, postPhysicsUpdateCB, quitCB);
    Diamond.dEngine2DLaunchGame();
}

/**
 * Frees all game and engine resources.
 */
exports.cleanUp = function() {
    Diamond.dGame2DDestroy();
    Diamond.dConfigDestroyAll();
    Diamond.dParticleSystem2DDestroy();
    Diamond.dRenderer2DDestroy();
    Diamond.dTransform2Destroy();
    Diamond.dEngine2DDestroy();
}

// Objects of the following classes reference their corresponding
// objects in the Diamond backend using a handle.

// TODO: test performance change from caching some values
// (ex. position data in transform) to return when getting
// and update when setting, in order to avoid making
// a call to the Diamond backend every time a value is read.
// This may not be worth it, because game logic seems to be more
// write-heavy than read-heavy (most of the read-heavy stuff
// probably happens in the physics engine, which is in the
// backend anyways, and in game logic-specific data
// that isn't stored in the Diamond backend).
exports.Transform2 = class Transform2 {
    constructor(position = {x: 0, y: 0},
                rotation = 0,
                scale    = {x: 1, y: 1}) {
        this.handle = Diamond.dTransform2MakeTransform(
            position.x, position.y, rotation, scale.x, scale.y
        );
    }
    destroy() {
        Diamond.dTransform2DestroyTransform(this.handle);
    }

    // note: setting position.x or position.y directly
    // will not change the underlying Diamond transform.
    // position can only be changed by setting position
    // to a new {x, y} object, or by using add function.
    get position() {
        return {
            x: Diamond.dTransform2GetPositionX(this.handle),
            y: Diamond.dTransform2GetPositionY(this.handle),
            add: function(dpos) {
                Diamond.dTransform2AddPosition(this.handle, dpos.x, dpos.y);
            },
            addX: function(dx) {
                Diamond.dTransform2AddPositionX(this.handle, dx);
            },
            addY: function(dy) {
                Diamond.dTransform2AddPositionY(this.handle, dy);
            }
        };
    }
    set position(position) {
        Diamond.dTransform2SetPosition(this.handle, position.x, position.y);
    }

    get rotation() {
        return Diamond.dTransform2GetRotation(this.handle);
    }
    set rotation(rotation) {
        Diamond.dTransform2SetRotation(this.handle, rotation);
    }

    // see comment for setting position.x and position.y,
    // applies to scale as well.
    get scale() {
        return {
            x: Diamond.dTransform2GetScaleX(this.handle),
            y: Diamond.dTransform2GetScaleY(this.handle)
        };
    }
    set scale(scale) {
        Diamond.dTransform2SetScale(this.handle, scale.x, scale.y);
    }

    // TODO: this doesn't seem to work
    toString() {
        return "handle: " + this.handle + ", " +
               "position: " + this.position.toString() + ", " +
               "rotation: " + this.rotation.toString() + ", " +
               "scale: " + this.scale.toString();
    }
}

exports.RenderComponent2D = class RenderComponent2D {
    constructor(transform, texture, layer = 0) {
        this.handle = Diamond.dRenderer2DMakeRenderComponent(
            transform.handle, texture.handle, layer
        );
    }
    destroy() {
        Diamond.dRenderer2DDestroyRenderComponent(this.handle);
    }

    // don't yet support getting a sprite- sorry
    set sprite(texture) {
        Diamond.dRenderComponent2DSetSprite(this.handle, texture.handle);
    }

    get layer() {
        return Diamond.dRenderComponent2DGetLayer(this.handle);
    }
    set layer(layer) {
        Diamond.dRenderComponent2DSetLayer(this.handle, layer);
    }

    get pivot() {
        return {
            x: Diamond.dRenderComponent2DGetPivotX(this.handle),
            y: Diamond.dRenderComponent2DGetPivotY(this.handle)
        };
    }
    set pivot(pivot) {
        Diamond.dRenderComponent2DSetPivot(this.handle, pivot.x, pivot.y);
    }

    flipX() {
        Diamond.dRenderComponent2DFlipX(this.handle);
    }
    flipY() {
        Diamond.dRenderComponent2DFlipY(this.handle);
    }

    get isFlippedX() {
        return Diamond.dRenderComponent2DIsFlippedX(this.handle);
    }
    get isFlippedY() {
        return Diamond.dRenderComponent2DIsFlippedY(this.handle);
    }
}

exports.renderer = {
    loadTexture: function(path) {
        const handle = Diamond.dRenderer2DLoadTexture(path);
        if (handle < 0)
            return null;
        return {handle: handle}
    },
    destroyTexture: function(texture) {
        Diamond.dRenderer2DDestroyTexture(texture.handle);
    },

    get resolution() {
        var xbuf = ref.alloc('int');
        var ybuf = ref.alloc('int');

        Diamond.dRenderer2DGetResolution(xbuf, ybuf);

        return {
            x: xbuf.deref(),
            y: ybuf.deref()
        };
    },
    get screenResolution() {
        var xbuf = ref.alloc('int');
        var ybuf = ref.alloc('int');

        Diamond.dRenderer2DGetScreenResolution(xbuf, ybuf);

        return {
            x: xbuf.deref(),
            y: ybuf.deref()
        };
    }
}

exports.ParticleEmitter2D = class ParticleEmitter2D {
    constructor(config, transform) {
        this.transform = transform;

        const configTable = Diamond.dConfigMakeConfigTable();

        // key to success!
        for (var key in config) {
            Diamond.dConfigSet(configTable, key, config[key].toString());
        }

        this.handle = Diamond.dParticleSystem2DMakeEmitter(
            configTable, transform.handle
        );

        Diamond.dConfigDestroyConfigTable(configTable);
    }
    // note: this does not destroy the particle emitter's
    // associated transform!
    destroy() {
        Diamond.dParticleSystem2DDestroyEmitter(this.handle);
    }
}

exports.Math = {
    RAD2DEG: 180 / Math.PI,
    DEG2RAD: Math.PI / 180
}

exports.Vector2 = {
    scalar: function(vec, scalar) {
        return {
            x: vec.x * scalar,
            y: vec.y * scalar
        };
    },
    scalarVec: function(vec, scalar) {
        return {
            x: vec.x * scalar.x,
            y: vec.y * scalar.y
        }
    },
    rotateVector: function(vec, radians) {
        const sinrad = Math.sin(radians);
        const cosrad = Math.cos(radians);
        return {
            x: vec.x * cosrad - vec.y * sinrad,
            y: vec.x * sinrad + vec.y * cosrad
        };
    }
}

exports.Util = {
    objToKeyvalPairs: function(obj) {
        var str = "";
        for (var prop in obj) {
            str += prop + ": " + obj[prop] + "\n";
        }
        return str;
    }
}
<commit_msg>Platform-conditional library finding<commit_after>/*
    Copyright 2017 Ahnaf Siddiqui

    Licensed under the Apache License, Version 2.0 (the "License");
    you may not use this file except in compliance with the License.
    You may obtain a copy of the License at

        http://www.apache.org/licenses/LICENSE-2.0

    Unless required by applicable law or agreed to in writing, software
    distributed under the License is distributed on an "AS IS" BASIS,
    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    See the License for the specific language governing permissions and
    limitations under the License.
*/

const ffi = require('ffi');
const ref = require('ref');

// Define types for Diamond lib interface
const intPtr = ref.refType('int');

// Determine the path to the Diamond lib
const platformdir = process.platform;
var libpath = __dirname + '/src/CDiamond/lib/' + platformdir;
if (platformdir == 'win32') {
    libpath += '/CDiamond';
}
else {
    libpath += '/libCDiamond';
}

// This is the bridge to native Diamond functions
const Diamond = ffi.Library(libpath, {
    // Engine2D
    'dEngine2DConfigureGraphics': ['void', ['string', 'int', 'int', 'bool', 'bool']],
    'dEngine2DConfigureAudio': ['void', ['int', 'int', 'int']],
    'dEngine2DInit': ['bool', []],
    'dEngine2DDestroy': ['void', []],
    'dEngine2DLaunchGame': ['void', []],
    // Game2D
    'dGame2DBenchmark': ['void', ['string']],
    'dGame2DInit': ['bool', ['pointer', 'pointer', 'pointer', 'pointer']],
    'dGame2DDestroy': ['void', []],
    // Transform2
    'dTransform2Init': ['bool', []],
    'dTransform2Destroy': ['void', []],
    'dTransform2MakeTransform': ['int', ['float', 'float', 'float', 'float', 'float']],
    'dTransform2DestroyTransform': ['void', ['int']],
    'dTransform2SetTransform': ['void', ['int', 'float', 'float', 'float', 'float', 'float']],
    'dTransform2SetPosition': ['void', ['int', 'float', 'float']],
    'dTransform2GetPositionX': ['float', ['int']],
    'dTransform2GetPositionY': ['float', ['int']],
    'dTransform2AddPosition': ['void', ['int', 'float', 'float']],
    'dTransform2AddPositionX': ['void', ['int', 'float']],
    'dTransform2AddPositionY': ['void', ['int', 'float']],
    'dTransform2GetRotation': ['float', ['int']],
    'dTransform2SetRotation': ['void', ['int', 'float']],
    'dTransform2AddRotation': ['void', ['int', 'float']],
    'dTransform2SetScale': ['void', ['int', 'float', 'float']],
    'dTransform2GetScaleX': ['float', ['int']],
    'dTransform2GetScaleY': ['float', ['int']],
    // Renderer2D
    'dRenderer2DInit': ['bool', []],
    'dRenderer2DDestroy': ['void', []],
    'dRenderer2DGetResolution': ['void', [intPtr, intPtr]],
    'dRenderer2DGetScreenResolution': ['void', [intPtr, intPtr]],
    'dRenderer2DLoadTexture': ['int', ['string']],
    'dRenderer2DDestroyTexture': ['void', ['int']],
    'dRenderer2DMakeRenderComponent': ['int', ['int', 'int', 'int']],
    'dRenderer2DDestroyRenderComponent': ['void', ['int']],
    'dRenderComponent2DSetSprite': ['void', ['int', 'int']],
    'dRenderComponent2DGetLayer': ['int', ['int']],
    'dRenderComponent2DSetLayer': ['void', ['int', 'int']],
    'dRenderComponent2DGetPivotX': ['float', ['int']],
    'dRenderComponent2DGetPivotY': ['float', ['int']],
    'dRenderComponent2DSetPivot': ['void', ['int', 'float', 'float']],
    'dRenderComponent2DFlipX': ['void', ['int']],
    'dRenderComponent2DFlipY': ['void', ['int']],
    'dRenderComponent2DIsFlippedX': ['bool', ['int']],
    'dRenderComponent2DIsFlippedY': ['bool', ['int']],
    // ParticleSystem2D
    'dParticleSystem2DInit': ['bool', ['int']],
    'dParticleSystem2DDestroy': ['void', []],
    'dParticleSystem2DMakeEmitter': ['int', ['int', 'int']],
    'dParticleSystem2DDestroyEmitter': ['void', ['int']],
    'dParticleSystem2DUpdate': ['void', ['int']],
    // Config
    'dConfigInitConfigLoader': ['void', ['string']],
    'dConfigDestroyAll': ['void', []],
    'dConfigMakeConfigTable': ['int', []],
    'dConfigLoadConfigTable': ['int', ['string']],
    'dConfigDestroyConfigTable': ['void', ['int']],
    'dConfigWriteConfig': ['void', ['int', 'string']],
    'dConfigHasKey': ['bool', ['int', 'string']],
    'dConfigGet': ['string', ['int', 'string']],
    'dConfigGetInt': ['int', ['int', 'string']],
    'dConfigGetFloat': ['float', ['int', 'string']],
    'dConfigGetBool': ['bool', ['int', 'string']],
    'dConfigSet': ['void', ['int', 'string', 'string']],
    'dConfigSetInt': ['void', ['int', 'string', 'int']],
    'dConfigSetFloat': ['void', ['int', 'string', 'float']],
    'dConfigSetBool': ['void', ['int', 'string', 'bool']]
});

// jdiamond API starts here

/**
 * Configuration for Diamond engine with default values.
 */
exports.Config = function() {
    this.windowTitle = "A Game Without a Name";
    this.windowWidth = 1280;
    this.windowHeight = 720;
    this.fullscreen = false;
    this.vsync = false;
    this.numAudioChannels = 2;
    this.audioFrequency = 44100; // hertz
    this.audioSampleSize = 2048; // bytes
    // estimate for the max number of particles
    // that may be present at any time.
    this.particlePoolSize = 100;
    this.benchmark = false;
    this.benchmarkFile = "benchmark.log";
}

/**
 * Initializes Diamond engine and its subsystems.
 */
exports.init = function(config) {
    var success = true;

    if (!config) {
        config = new exports.Config();
    }

    Diamond.dEngine2DConfigureGraphics(
        config.windowTitle,
        config.windowWidth,
        config.windowHeight,
        config.fullscreen,
        config.vsync
    );
    Diamond.dEngine2DConfigureAudio(
        config.numAudioChannels,
        config.audioFrequency,
        config.audioSampleSize
    );

    if (config.benchmark)
        Diamond.dGame2DBenchmark(config.benchmarkFile);

    if (!(Diamond.dEngine2DInit() &&
          Diamond.dTransform2Init() &&
          Diamond.dRenderer2DInit() &&
          Diamond.dParticleSystem2DInit(config.particlePoolSize))) {
        success = false;
    }

    Diamond.dConfigInitConfigLoader("");

    return success;
}

/**
 * Begins the game loop in the Diamond backend.
 */
exports.launch = function(update, postPhysicsUpdate, quit) {
    var postPhysicsUpdateCB = ref.NULL;
    var quitCB = ref.NULL;

    const updateCB = ffi.Callback('void', ['int'], function(delta) {
        if (update) {
            update(delta);
        }

        // update Diamond's non-core systems (ex. particles)
        // that aren't automatically updated within
        // Diamond Engine's game loop.
        Diamond.dParticleSystem2DUpdate(delta);
    });

    if (postPhysicsUpdate) {
        postPhysicsUpdateCB = ffi.Callback('void', ['int'], postPhysicsUpdate);
    }
    if (quit) {
        quitCB = ffi.Callback('void', [], quit);
    }

    Diamond.dGame2DInit(ref.NULL, updateCB, postPhysicsUpdateCB, quitCB);
    Diamond.dEngine2DLaunchGame();
}

/**
 * Frees all game and engine resources.
 */
exports.cleanUp = function() {
    Diamond.dGame2DDestroy();
    Diamond.dConfigDestroyAll();
    Diamond.dParticleSystem2DDestroy();
    Diamond.dRenderer2DDestroy();
    Diamond.dTransform2Destroy();
    Diamond.dEngine2DDestroy();
}

// Objects of the following classes reference their corresponding
// objects in the Diamond backend using a handle.

// TODO: test performance change from caching some values
// (ex. position data in transform) to return when getting
// and update when setting, in order to avoid making
// a call to the Diamond backend every time a value is read.
// This may not be worth it, because game logic seems to be more
// write-heavy than read-heavy (most of the read-heavy stuff
// probably happens in the physics engine, which is in the
// backend anyways, and in game logic-specific data
// that isn't stored in the Diamond backend).
exports.Transform2 = class Transform2 {
    constructor(position = {x: 0, y: 0},
                rotation = 0,
                scale    = {x: 1, y: 1}) {
        this.handle = Diamond.dTransform2MakeTransform(
            position.x, position.y, rotation, scale.x, scale.y
        );
    }
    destroy() {
        Diamond.dTransform2DestroyTransform(this.handle);
    }

    // note: setting position.x or position.y directly
    // will not change the underlying Diamond transform.
    // position can only be changed by setting position
    // to a new {x, y} object, or by using add function.
    get position() {
        return {
            x: Diamond.dTransform2GetPositionX(this.handle),
            y: Diamond.dTransform2GetPositionY(this.handle),
            add: function(dpos) {
                Diamond.dTransform2AddPosition(this.handle, dpos.x, dpos.y);
            },
            addX: function(dx) {
                Diamond.dTransform2AddPositionX(this.handle, dx);
            },
            addY: function(dy) {
                Diamond.dTransform2AddPositionY(this.handle, dy);
            }
        };
    }
    set position(position) {
        Diamond.dTransform2SetPosition(this.handle, position.x, position.y);
    }

    get rotation() {
        return Diamond.dTransform2GetRotation(this.handle);
    }
    set rotation(rotation) {
        Diamond.dTransform2SetRotation(this.handle, rotation);
    }

    // see comment for setting position.x and position.y,
    // applies to scale as well.
    get scale() {
        return {
            x: Diamond.dTransform2GetScaleX(this.handle),
            y: Diamond.dTransform2GetScaleY(this.handle)
        };
    }
    set scale(scale) {
        Diamond.dTransform2SetScale(this.handle, scale.x, scale.y);
    }

    // TODO: this doesn't seem to work
    toString() {
        return "handle: " + this.handle + ", " +
               "position: " + this.position.toString() + ", " +
               "rotation: " + this.rotation.toString() + ", " +
               "scale: " + this.scale.toString();
    }
}

exports.RenderComponent2D = class RenderComponent2D {
    constructor(transform, texture, layer = 0) {
        this.handle = Diamond.dRenderer2DMakeRenderComponent(
            transform.handle, texture.handle, layer
        );
    }
    destroy() {
        Diamond.dRenderer2DDestroyRenderComponent(this.handle);
    }

    // don't yet support getting a sprite- sorry
    set sprite(texture) {
        Diamond.dRenderComponent2DSetSprite(this.handle, texture.handle);
    }

    get layer() {
        return Diamond.dRenderComponent2DGetLayer(this.handle);
    }
    set layer(layer) {
        Diamond.dRenderComponent2DSetLayer(this.handle, layer);
    }

    get pivot() {
        return {
            x: Diamond.dRenderComponent2DGetPivotX(this.handle),
            y: Diamond.dRenderComponent2DGetPivotY(this.handle)
        };
    }
    set pivot(pivot) {
        Diamond.dRenderComponent2DSetPivot(this.handle, pivot.x, pivot.y);
    }

    flipX() {
        Diamond.dRenderComponent2DFlipX(this.handle);
    }
    flipY() {
        Diamond.dRenderComponent2DFlipY(this.handle);
    }

    get isFlippedX() {
        return Diamond.dRenderComponent2DIsFlippedX(this.handle);
    }
    get isFlippedY() {
        return Diamond.dRenderComponent2DIsFlippedY(this.handle);
    }
}

exports.renderer = {
    loadTexture: function(path) {
        const handle = Diamond.dRenderer2DLoadTexture(path);
        if (handle < 0)
            return null;
        return {handle: handle}
    },
    destroyTexture: function(texture) {
        Diamond.dRenderer2DDestroyTexture(texture.handle);
    },

    get resolution() {
        var xbuf = ref.alloc('int');
        var ybuf = ref.alloc('int');

        Diamond.dRenderer2DGetResolution(xbuf, ybuf);

        return {
            x: xbuf.deref(),
            y: ybuf.deref()
        };
    },
    get screenResolution() {
        var xbuf = ref.alloc('int');
        var ybuf = ref.alloc('int');

        Diamond.dRenderer2DGetScreenResolution(xbuf, ybuf);

        return {
            x: xbuf.deref(),
            y: ybuf.deref()
        };
    }
}

exports.ParticleEmitter2D = class ParticleEmitter2D {
    constructor(config, transform) {
        this.transform = transform;

        const configTable = Diamond.dConfigMakeConfigTable();

        // key to success!
        for (var key in config) {
            Diamond.dConfigSet(configTable, key, config[key].toString());
        }

        this.handle = Diamond.dParticleSystem2DMakeEmitter(
            configTable, transform.handle
        );

        Diamond.dConfigDestroyConfigTable(configTable);
    }
    // note: this does not destroy the particle emitter's
    // associated transform!
    destroy() {
        Diamond.dParticleSystem2DDestroyEmitter(this.handle);
    }
}

exports.Math = {
    RAD2DEG: 180 / Math.PI,
    DEG2RAD: Math.PI / 180
}

exports.Vector2 = {
    scalar: function(vec, scalar) {
        return {
            x: vec.x * scalar,
            y: vec.y * scalar
        };
    },
    scalarVec: function(vec, scalar) {
        return {
            x: vec.x * scalar.x,
            y: vec.y * scalar.y
        }
    },
    rotateVector: function(vec, radians) {
        const sinrad = Math.sin(radians);
        const cosrad = Math.cos(radians);
        return {
            x: vec.x * cosrad - vec.y * sinrad,
            y: vec.x * sinrad + vec.y * cosrad
        };
    }
}

exports.Util = {
    objToKeyvalPairs: function(obj) {
        var str = "";
        for (var prop in obj) {
            str += prop + ": " + obj[prop] + "\n";
        }
        return str;
    }
}
<|endoftext|>


<commit_before>// VISUALIZATION
// ------------------------------------------

var stageWidth = 1600;
var stageHeight = 12000;
var stageSvg = null;

function visualizationInit() {
    var margin = {top: 20, right: 500, bottom: 0, left: 20};

    stageSvg = d3.select("body").append("svg")
        .attr("width", stageWidth + margin.left + margin.right)
        .attr("height", stageHeight + margin.top + margin.bottom)
        .style("margin-left", margin.left + "px")
        .append("g")
        .attr("transform", "translate(" + margin.left + "," + margin.top + ")");
}

var visualize = function visualize(interval, data) {

    var intervalSeconds = interval * 60 * 1000;

    function truncate(str, maxLength, suffix) {
        if (str.length > maxLength) {
            str = str.substring(0, maxLength + 1);
            str = str.substring(0, Math.min(str.length, str.lastIndexOf(" ")));
            str = str + suffix;
        }
        return str;
    }

    data = data['aggregations']['timeframes']['buckets'];
    var timeframesCount = data.length;
    var clusterList = [];
    data.forEach(function (timeframeData) {
        timeframeData['timeframe_start'] = new Date(timeframeData['key']);
        timeframeData['timeframe_end'] = new Date((timeframeData['key'] + intervalSeconds));
        timeframeData['behaviors']['value']['clusters'].forEach(function (cluster) {
            var timeframe = [
                timeframeData['timeframe_start'],
                timeframeData['timeframe_end'],
                cluster['size'],
                timeframeData['behaviors']['value']['clusters_users_count']
            ];
            var percentValue = Math.round((timeframe[2]/timeframe[3])*10000)/100;
            if(percentValue > 1) {
                var representant = cluster['representants'][0];
                if (clusterList.hasOwnProperty(representant.pathHash)) {
                    clusterList[representant.pathHash].timeframes.push(timeframe)
                } else {
                    clusterList[representant.pathHash] = {
                        "timeframes": [
                            timeframe
                        ],
                        "path": representant['path'],
                        "name": representant['path'].join(', ')
                    }
                }
            }
        });
    });
    console.log(clusterList);
    // fill clusters between timeframes
    data.forEach(function (timeframeData) {
        timeframeData['timeframe_start'] = new Date(timeframeData['key']);
        timeframeData['timeframe_end'] = new Date((timeframeData['key'] + intervalSeconds));
        timeframeData['behaviors']['value']['clusters'].forEach(function (timeFrameCluster) {
            var timeframe = [
                timeframeData['timeframe_start'],
                timeframeData['timeframe_end'],
                timeFrameCluster['size'],
                timeframeData['behaviors']['value']['clusters_users_count']
            ];
            var percentValue = Math.round((timeframe[2]/timeframe[3])*10000)/100;
            if(percentValue > 1) {
                var representant = timeFrameCluster['representants'][0];
                var path = representant['path'];
                var name = path.join(', ');
                for (var key in clusterList) {
                    cluster = clusterList[key];
                    if(path.length > cluster.path.length && name.indexOf(cluster.name) !== -1) {
                        var found = false;
                        for (var timeframeKey in cluster.timeframes) {
                            if(cluster.timeframes[timeframeKey][0].getTime() == timeframe[0].getTime() && cluster.timeframes[timeframeKey][1].getTime() == timeframe[1].getTime()) {
                                cluster.timeframes[timeframeKey][2] = cluster.timeframes[timeframeKey][2] + timeframe[2];
                                console.log("boost " + cluster.name + " with " + name + " by " + timeframe[2] + " on " + timeframe[0]);
                                found = true;
                            }
                        }
                        if(!found) {
                        console.log("boost " + cluster.name + " with " + name + " by " + timeframe[2] + " on " + timeframe[0]);
                            clusterList[key].timeframes.push(timeframe);
                        }
                    }
                }
            }
        });
    });
    clusterList = clusterList.sort(function(a, b) {
        console.log("sorting");
        var aSum = a.timeframes.reduce( function(prev, next){
           return prev + next[2];
        }, 0);
        var bSum = b.timeframes.reduce( function(prev, next){
           return prev + next[2];
        }, 0);
        console.log(a.name + ": " + aSum);
        console.log(b.name + ": " + bSum);
        return parseFloat(aSum) - parseFloat(bSum);
    });
    data = [];
    for (var key in clusterList) {
        if (!clusterList.hasOwnProperty(key)) continue;
        data.push(clusterList[key]);
    }
    clusterList = null;

    var xMin = d3.min(data, function (cluster) {
        return d3.min(cluster['timeframes'], function (timeframe) {
            return timeframe[0];
        });
    });
    var xMax = d3.max(data, function (cluster) {
        return d3.max(cluster['timeframes'], function (timeframe) {
            return timeframe[0];
        });
    });
    console.log(xMin);
    console.log(xMax);

    var xScale = d3.time.scale()
        .domain([xMin, xMax])
        .range([0, stageWidth]);

    var timeFormat = d3.time.format("%m/%d %H:%M");
    xAxis = d3.svg.axis()
        .scale(xScale)
        .orient("top")
        .ticks(timeframesCount)
        .tickPadding(5)
        .tickFormat(timeFormat);

    stageSvg.append("g")
        .attr("class", "x axis")
        .attr("transform", "translate(0," + 0 + ")")
        .call(xAxis);

    var c = d3.scale.category20c();

    for (var j = 0; j < data.length; j++) {
        var g = stageSvg.append("g").attr("class", "journal");

        var circles = g.selectAll("circle")
            .data(data[j]['timeframes'])
            .enter()
            .append("circle");

        var text = g.selectAll("text")
            .data(data[j]['timeframes'])
            .enter()
            .append("text");

        var rScale = d3.scale.linear()
            .domain([0, d3.max(data[j]['timeframes'], function (d) {
                return d[3];
            })])
            .range([6, 14]);

        circles
            .attr("cx", function (d, i) {
                return xScale(d[0]);
            })
            .attr("cy", j * 30 + 20)
            .attr("r", function (d) {
                return rScale(d[2]);
            })
            .style("fill", function (d) {
                return c(j);
            });

        text
            .attr("y", j * 30 + 25)
            .attr("x", function (d, i) {
                return xScale(d[0]) - 5;
            })
            .attr("class", "value")
            .text(function (d) {
                return (Math.round((d[2]/d[3])*10000)/100)+"%";
            })
            .style("fill", function (d) {
                return c(j);
            })
            .style("display", "none");

        g.append("text")
            .attr("y", j * 30 + 25)
            .attr("x", stageWidth + 50)
            .attr("class", "label")
            .text(truncate(data[j]['name'], 60, "..."))
            .style("fill", function (d) {
                return c(j);
            })
            .on("mouseover", mouseover)
            .on("mouseout", mouseout);
    }
    ;

    function mouseover(p) {
        var g = d3.select(this).node().parentNode;
        d3.select(g).selectAll("circle").style("display", "none");
        d3.select(g).selectAll("text.value").style("display", "block");
    }

    function mouseout(p) {
        var g = d3.select(this).node().parentNode;
        d3.select(g).selectAll("circle").style("display", "block");
        d3.select(g).selectAll("text.value").style("display", "none");
    }
};<commit_msg>adjustments<commit_after>// VISUALIZATION
// ------------------------------------------

var stageWidth = 1600;
var stageHeight = 12000;
var stageSvg = null;

function visualizationInit() {
    var margin = {top: 20, right: 500, bottom: 0, left: 20};

    stageSvg = d3.select("body").append("svg")
        .attr("width", stageWidth + margin.left + margin.right)
        .attr("height", stageHeight + margin.top + margin.bottom)
        .style("margin-left", margin.left + "px")
        .append("g")
        .attr("transform", "translate(" + margin.left + "," + margin.top + ")");
}

var visualize = function visualize(interval, data) {

    var intervalSeconds = interval * 60 * 1000;

    function truncate(str, maxLength, suffix) {
        if (str.length > maxLength) {
            str = str.substring(0, maxLength + 1);
            str = str.substring(0, Math.min(str.length, str.lastIndexOf(" ")));
            str = str + suffix;
        }
        return str;
    }

    data = data['aggregations']['timeframes']['buckets'];
    var timeframesCount = data.length;
    var clusterList = [];
    data.forEach(function (timeframeData) {
        timeframeData['timeframe_start'] = new Date(timeframeData['key']);
        timeframeData['timeframe_end'] = new Date((timeframeData['key'] + intervalSeconds));
        timeframeData['behaviors']['value']['clusters'].forEach(function (cluster) {
            var timeframe = [
                timeframeData['timeframe_start'],
                timeframeData['timeframe_end'],
                cluster['size'],
                timeframeData['behaviors']['value']['clusters_users_count']
            ];
            var percentValue = Math.round((timeframe[2]/timeframe[3])*10000)/100;
            if(percentValue > 1) {
                var representant = cluster['representants'][0];
                if (clusterList.hasOwnProperty(representant.pathHash)) {
                    clusterList[representant.pathHash].timeframes.push(timeframe)
                } else {
                    clusterList[representant.pathHash] = {
                        "timeframes": [
                            timeframe
                        ],
                        "path": representant['path'],
                        "name": representant['path'].join(', ')
                    }
                }
            }
        });
    });
    console.log(clusterList);
    // fill clusters between timeframes
    data.forEach(function (timeframeData) {
        timeframeData['timeframe_start'] = new Date(timeframeData['key']);
        timeframeData['timeframe_end'] = new Date((timeframeData['key'] + intervalSeconds));
        timeframeData['behaviors']['value']['clusters'].forEach(function (timeFrameCluster) {
            var timeframe = [
                timeframeData['timeframe_start'],
                timeframeData['timeframe_end'],
                timeFrameCluster['size'],
                timeframeData['behaviors']['value']['clusters_users_count']
            ];
            var percentValue = Math.round((timeframe[2]/timeframe[3])*10000)/100;
            if(percentValue > 1) {
                var representant = timeFrameCluster['representants'][0];
                var path = representant['path'];
                var name = path.join(', ');
                for (var key in clusterList) {
                    cluster = clusterList[key];
                    if(path.length > cluster.path.length && name.indexOf(cluster.name) !== -1) {
                        var found = false;
                        for (var timeframeKey in cluster.timeframes) {
                            if(cluster.timeframes[timeframeKey][0].getTime() == timeframe[0].getTime() && cluster.timeframes[timeframeKey][1].getTime() == timeframe[1].getTime()) {
                                cluster.timeframes[timeframeKey][2] = cluster.timeframes[timeframeKey][2] + timeframe[2];
                                console.log("boost " + cluster.name + "("+cluster.timeframes[timeframeKey][2]+") with " + name + " by " + timeframe[2] + " on " + timeframe[0]);
                                found = true;
                            }
                        }
                        if(!found) {
                        console.log("boost " + cluster.name + " with " + name + " by " + timeframe[2] + " on " + timeframe[0]);
                            clusterList[key].timeframes.push(timeframe);
                        }
                    }
                }
            }
        });
    });
    clusterList = clusterList.sort(function(a, b) {
        console.log("sorting");
        var aSum = a.timeframes.reduce( function(prev, next){
           return prev + next[2];
        }, 0);
        var bSum = b.timeframes.reduce( function(prev, next){
           return prev + next[2];
        }, 0);
        console.log(a.name + ": " + aSum);
        console.log(b.name + ": " + bSum);
        return parseFloat(aSum) - parseFloat(bSum);
    });
    data = [];
    for (var key in clusterList) {
        if (!clusterList.hasOwnProperty(key)) continue;
        data.push(clusterList[key]);
    }
    clusterList = null;

    var xMin = d3.min(data, function (cluster) {
        return d3.min(cluster['timeframes'], function (timeframe) {
            return timeframe[0];
        });
    });
    var xMax = d3.max(data, function (cluster) {
        return d3.max(cluster['timeframes'], function (timeframe) {
            return timeframe[0];
        });
    });
    console.log(xMin);
    console.log(xMax);

    var xScale = d3.time.scale()
        .domain([xMin, xMax])
        .range([0, stageWidth]);

    var timeFormat = d3.time.format("%m/%d %H:%M");
    xAxis = d3.svg.axis()
        .scale(xScale)
        .orient("top")
        .ticks(timeframesCount)
        .tickPadding(5)
        .tickFormat(timeFormat);

    stageSvg.append("g")
        .attr("class", "x axis")
        .attr("transform", "translate(0," + 0 + ")")
        .call(xAxis);

    var c = d3.scale.category20c();

    for (var j = 0; j < data.length; j++) {
        var g = stageSvg.append("g").attr("class", "journal");

        var circles = g.selectAll("circle")
            .data(data[j]['timeframes'])
            .enter()
            .append("circle");

        var text = g.selectAll("text")
            .data(data[j]['timeframes'])
            .enter()
            .append("text");

        var rScale = d3.scale.linear()
            .domain([0, d3.max(data[j]['timeframes'], function (d) {
                return d[3];
            })])
            .range([6, 14]);

        circles
            .attr("cx", function (d, i) {
                return xScale(d[0]);
            })
            .attr("cy", j * 30 + 20)
            .attr("r", function (d) {
                return rScale(d[2]);
            })
            .style("fill", function (d) {
                return c(j);
            });

        text
            .attr("y", j * 30 + 25)
            .attr("x", function (d, i) {
                return xScale(d[0]) - 5;
            })
            .attr("class", "value")
            .text(function (d) {
                return (Math.round((d[2]/d[3])*10000)/100)+"%";
            })
            .style("fill", function (d) {
                return c(j);
            })
            .style("display", "none");

        g.append("text")
            .attr("y", j * 30 + 25)
            .attr("x", stageWidth + 50)
            .attr("class", "label")
            .text(truncate(data[j]['name'], 60, "..."))
            .style("fill", function (d) {
                return c(j);
            })
            .on("mouseover", mouseover)
            .on("mouseout", mouseout);
    }
    ;

    function mouseover(p) {
        var g = d3.select(this).node().parentNode;
        d3.select(g).selectAll("circle").style("display", "none");
        d3.select(g).selectAll("text.value").style("display", "block");
    }

    function mouseout(p) {
        var g = d3.select(this).node().parentNode;
        d3.select(g).selectAll("circle").style("display", "block");
        d3.select(g).selectAll("text.value").style("display", "none");
    }
};<|endoftext|>


<commit_before>'use strict';

const program = require('commander');
const Promise = require("bluebird");
const fsa = Promise.promisifyAll(require("fs"));
const archiver = require('archiver');
const path = require('path');

const mkdirpAsync = Promise.promisify(require('mkdirp'));
const zipFileUtils = require('../servers/lib/zipFileUtils.js');

//const defaultPort =  29015; // true default
const defaultPort =  30815; // localhost version

let parameters = getControlParameters();

var port = parameters.port;
var base = parameters.base;
var zipbase = parameters.zipbase;
var replace = !!parameters.replace;
var idList = parameters.id;
var all = !!parameters.all;
var numberProcessed = 0;
var totalNumberToProcess = 0;

main();

function main() {

    verifyParameters();

    reportParameters();

    if (base) {
        zipFileUtils.setBase(base);
    }
    if (zipbase){
        zipFileUtils.setZipDirPath(zipbase);
    }

    Promise.coroutine(buildZipFiles)();
}

function* buildZipFiles() {
    try {
        console.log("setup");
        var r = require('rethinkdbdash')({
            db: 'materialscommons',
            port: port
        });
        console.log("setup done");

        var idSet = new Set(idList);
        var ids = (idList.length > 0);

        let allDatasetIds = yield r.db('materialscommons').table('datasets').pluck(["id","published"]);

        var idsToProcess = [];
        allDatasetIds.forEach(record => {
            let id = record['id'];
            if (all || (ids && (idSet.has(id)))) {
                if (record['published']) {
                    idsToProcess.push(id);
                } else {
                    console.log("No zip will be created for unpublished dataset: " + id);
                }
            }
        });

        console.log("Zip files to be created for " + idsToProcess.length + " datasets");

        if (idsToProcess.length == 0) {
            return Promise.reject("Error: no dataset id values to process");
        }

        totalNumberToProcess = idsToProcess.length;

        for (var i = 0; i < idsToProcess.length; i++) {
            var id = idsToProcess[i];
            Promise.coroutine(
                publishDatasetZipFile
            )(r, id);
        }

    } catch (error) {
        console.log("Error in buildZipFiles: ", error)
    }
}

function* publishDatasetZipFile(r, datasetId) {
    try {
        console.log("zip", datasetId);
        let ds = yield r.db('materialscommons').table('datasets').get(datasetId);
        let zipDirPath = zipFileUtils.zipDirPath(ds);
        let fillPathAndFilename = zipFileUtils.fullPathAndFilename(ds);

        console.log("full path and filename: ", fillPathAndFilename);

        let ds2dfEntries = yield r.db('materialscommons').table('dataset2datafile')
            .getAll(datasetId, {index: 'dataset_id'}).coerceTo('array');

        if (!ds2dfEntries.length) {
            numberProcessed++;
            console.log('no zip for id = ' + datasetId);
            console.log('total number of zip files processed: ' + numberProcessed + " of " + totalNumberToProcess);
            return new Promise(function(resolve, reject){resolve()});
        }

        yield mkdirpAsync(zipDirPath);

        let datafileIds = ds2dfEntries.map(entry => entry.datafile_id);
        let datafiles = yield r.table('datafiles').getAll(r.args(datafileIds));

        console.log("For id = " + datasetId + ", there are " + datafileIds.length + " files");

        let nameSourceList = [];
        var seenThisOne = {};

        for (var i=0; i < datafiles.length; i++) {
            let df = datafiles[i];

            let zipEntry = zipFileUtils.zipEntry(df); // sets fileName, checksum, sourcePath
            console.log("zipEntry: ", zipEntry);
            let path = zipEntry.sourcePath;
            let name = zipEntry.fileName;
            let checksum = zipEntry.checksum;
            name = resolveZipfileFilenameDuplicates(seenThisOne, name, checksum);
            console.log("before read stream");
            let stream = yield fsa.createReadStreamAsync(path,{
                flags: 'r',
                encoding: null,
                fd: null,
                mode: 0o666,
                autoClose: true
            });
            console.log("after read stream: ", path);
            nameSourceList.push({name: name, source: source});
        }

        console.log("got files to add: " + nameSourceList.length);

        var output = yield fsa.createWriteStreamAsync(fillPathAndFilename);

        console.log("output stream set");

        let retP =  new Promise(function (resolve, reject) {
            var archive = archiver('zip');

            output.on('close', function () {
                console.log('for dataset: ' + datasetId + " with " + archive.pointer() + ' total bytes');
                numberProcessed++;
                console.log('total number of zip files processed: ' + numberProcessed + " of " + totalNumberToProcess);
                resolve();
            });

            archive.on('error', reject);

            archive.on('close',function() {console.log('archive close');});

            archive.pipe(output);

            nameSourceList.forEach(ns => {
                console.log("name: ", ns.name);
                archive.append(ns.source, {name:ns.name} );
            });

            archive.finalize();
        });

        console.log("Got return promise");

        return retP;

    } catch (error) {
        return yield Promise.reject("Error in publishDatasetZipFile: " +  error.message);
    }
}

function resolveZipfileFilenameDuplicates(seenThisOne,name,checksum){
    name = name.toLowerCase();

    if (name.startsWith(".")) {
        name = "dot" + name;
    }

    if (name in seenThisOne) {
        var count = 0;
        if (seenThisOne[name] == checksum) {
            console.log("Seen this file before: " + name);
            return null;
        } else {
            let newName = resolveZipfileFilenameUnique(name, count);
            while (newName in seenThisOne) {
                count++;
                newName = resolveZipfileFilenameUnique(name, count);
            }
            // console.log(name + " --> " + newName);
            name = newName;
        }
    }
    seenThisOne[name] = checksum;
    return name;
}

function resolveZipfileFilenameUnique(name, count) {
    let parts = path.parse(name);
    return parts.name + "_" + count + parts.ext;
}

function verifyParameters() {
    var ids = (idList.length > 0);

    if (!all && !ids) {
        program.help(text => {
            var banner =    "\t--------------------------------\n";
            return banner + "\tOne of --id or --all is required\n"
                + banner + text;
        })
    }

    if (ids && all) {
        console.log("WARNING: --all (or -a) was specified, but one of more id valeus were given\n"
            + "\tThe id values take precident");
    }

    if (ids) {
        all = false;
    }

}

function reportParameters() {
    console.log("use --help for list of parameter options");
    console.log("port = " + port);
    console.log("base = " + base);
    console.log("zipbase = " + zipbase);
    console.log("replace = " + replace);
    if (all) {
        console.log("process all")
    } else {
        console.log("input id(s)");
        idList.forEach(id => {
            console.log("\tid = " + id);
        });
    }
}

function getControlParameters() {
    let accumulateIds = (id,ids) => {
        ids.push(id);
        return ids;
    };

    return program
        .option('-p, --port [port]', 'The RethinkDB port; defaults to ' + defaultPort,defaultPort)
        .option('-b, --base [base]', 'The base path of the datasets directory, optional')
        .option('-z, --zipbase [zipbase]', 'The base path for the zipfile directory, optional')
        .option('-i, --id [id]', 'If given, the id of the dataset to copy, can be repeated',accumulateIds,[])
        .option('-a, --all', 'If given, and no id(s), then copy all datasets, \n\t\tone of --id or --all required')
        .option('-r, --replace','If given, then replace files already generaåted, \n\t\totherwise not; optional')
        .parse(process.argv);
}<commit_msg>does this work?<commit_after>'use strict';

const program = require('commander');
const Promise = require("bluebird");
const fsa = Promise.promisifyAll(require("fs"));
const archiver = require('archiver');
const path = require('path');

const mkdirpAsync = Promise.promisify(require('mkdirp'));
const zipFileUtils = require('../servers/lib/zipFileUtils.js');

//const defaultPort =  29015; // true default
const defaultPort =  30815; // localhost version

let parameters = getControlParameters();

var port = parameters.port;
var base = parameters.base;
var zipbase = parameters.zipbase;
var replace = !!parameters.replace;
var idList = parameters.id;
var all = !!parameters.all;
var numberProcessed = 0;
var totalNumberToProcess = 0;

main();

function main() {

    verifyParameters();

    reportParameters();

    if (base) {
        zipFileUtils.setBase(base);
    }
    if (zipbase){
        zipFileUtils.setZipDirPath(zipbase);
    }

    Promise.coroutine(buildZipFiles)();
}

function* buildZipFiles() {
    try {
        console.log("setup");
        var r = require('rethinkdbdash')({
            db: 'materialscommons',
            port: port
        });
        console.log("setup done");

        var idSet = new Set(idList);
        var ids = (idList.length > 0);

        let allDatasetIds = yield r.db('materialscommons').table('datasets').pluck(["id","published"]);

        var idsToProcess = [];
        allDatasetIds.forEach(record => {
            let id = record['id'];
            if (all || (ids && (idSet.has(id)))) {
                if (record['published']) {
                    idsToProcess.push(id);
                } else {
                    console.log("No zip will be created for unpublished dataset: " + id);
                }
            }
        });

        console.log("Zip files to be created for " + idsToProcess.length + " datasets");

        if (idsToProcess.length == 0) {
            return Promise.reject("Error: no dataset id values to process");
        }

        totalNumberToProcess = idsToProcess.length;

        for (var i = 0; i < idsToProcess.length; i++) {
            var id = idsToProcess[i];
            Promise.coroutine(
                publishDatasetZipFile
            )(r, id);
        }

    } catch (error) {
        console.log("Error in buildZipFiles: ", error)
    }
}

function* publishDatasetZipFile(r, datasetId) {
    try {
        console.log("zip", datasetId);
        let ds = yield r.db('materialscommons').table('datasets').get(datasetId);
        let zipDirPath = zipFileUtils.zipDirPath(ds);
        let fillPathAndFilename = zipFileUtils.fullPathAndFilename(ds);

        console.log("full path and filename: ", fillPathAndFilename);

        let ds2dfEntries = yield r.db('materialscommons').table('dataset2datafile')
            .getAll(datasetId, {index: 'dataset_id'}).coerceTo('array');

        if (!ds2dfEntries.length) {
            numberProcessed++;
            console.log('no zip for id = ' + datasetId);
            console.log('total number of zip files processed: ' + numberProcessed + " of " + totalNumberToProcess);
            return new Promise(function(resolve, reject){resolve()});
        }

        yield mkdirpAsync(zipDirPath);

        let datafileIds = ds2dfEntries.map(entry => entry.datafile_id);
        let datafiles = yield r.table('datafiles').getAll(r.args(datafileIds));

        console.log("For id = " + datasetId + ", there are " + datafileIds.length + " files");

        let nameSourceList = [];
        var seenThisOne = {};

        for (var i=0; i < datafiles.length; i++) {
            let df = datafiles[i];

            let zipEntry = zipFileUtils.zipEntry(df); // sets fileName, checksum, sourcePath
            console.log("zipEntry: ", zipEntry);
            let path = zipEntry.sourcePath;
            let name = zipEntry.fileName;
            let checksum = zipEntry.checksum;
            name = resolveZipfileFilenameDuplicates(seenThisOne, name, checksum);
            console.log("before read stream");
            let stream = yield fsa.createReadStreamAsync(path,{
                flags: 'r',
                encoding: null,
                fd: null,
                mode: 0o666,
                autoClose: true
            });
            console.log("after read stream: ", path);
            nameSourceList.push({name: name, source: source});
        }

        console.log("got files to add: " + nameSourceList.length);

        var output = yield fsa.createWriteStreamAsync(fillPathAndFilename);

        console.log("output stream set");

        let retP =  new Promise(function (resolve, reject) {
            var archive = archiver('zip');

            output.on('close', function () {
                console.log('for dataset: ' + datasetId + " with " + archive.pointer() + ' total bytes');
                numberProcessed++;
                console.log('total number of zip files processed: ' + numberProcessed + " of " + totalNumberToProcess);
                resolve();
                if (numberProcessed == totalNumberToProcess) {
                    process.exit(0);
                }
            });

            archive.on('error', reject);

            archive.on('close',function() {console.log('archive close');});

            archive.pipe(output);

            nameSourceList.forEach(ns => {
                console.log("name: ", ns.name);
                archive.append(ns.source, {name:ns.name} );
            });

            archive.finalize();
        });

        console.log("Got return promise");

        return retP;

    } catch (error) {
        return yield Promise.reject("Error in publishDatasetZipFile: " +  error.message);
    }
}

function resolveZipfileFilenameDuplicates(seenThisOne,name,checksum){
    name = name.toLowerCase();

    if (name.startsWith(".")) {
        name = "dot" + name;
    }

    if (name in seenThisOne) {
        var count = 0;
        if (seenThisOne[name] == checksum) {
            console.log("Seen this file before: " + name);
            return null;
        } else {
            let newName = resolveZipfileFilenameUnique(name, count);
            while (newName in seenThisOne) {
                count++;
                newName = resolveZipfileFilenameUnique(name, count);
            }
            // console.log(name + " --> " + newName);
            name = newName;
        }
    }
    seenThisOne[name] = checksum;
    return name;
}

function resolveZipfileFilenameUnique(name, count) {
    let parts = path.parse(name);
    return parts.name + "_" + count + parts.ext;
}

function verifyParameters() {
    var ids = (idList.length > 0);

    if (!all && !ids) {
        program.help(text => {
            var banner =    "\t--------------------------------\n";
            return banner + "\tOne of --id or --all is required\n"
                + banner + text;
        })
    }

    if (ids && all) {
        console.log("WARNING: --all (or -a) was specified, but one of more id valeus were given\n"
            + "\tThe id values take precident");
    }

    if (ids) {
        all = false;
    }

}

function reportParameters() {
    console.log("use --help for list of parameter options");
    console.log("port = " + port);
    console.log("base = " + base);
    console.log("zipbase = " + zipbase);
    console.log("replace = " + replace);
    if (all) {
        console.log("process all")
    } else {
        console.log("input id(s)");
        idList.forEach(id => {
            console.log("\tid = " + id);
        });
    }
}

function getControlParameters() {
    let accumulateIds = (id,ids) => {
        ids.push(id);
        return ids;
    };

    return program
        .option('-p, --port [port]', 'The RethinkDB port; defaults to ' + defaultPort,defaultPort)
        .option('-b, --base [base]', 'The base path of the datasets directory, optional')
        .option('-z, --zipbase [zipbase]', 'The base path for the zipfile directory, optional')
        .option('-i, --id [id]', 'If given, the id of the dataset to copy, can be repeated',accumulateIds,[])
        .option('-a, --all', 'If given, and no id(s), then copy all datasets, \n\t\tone of --id or --all required')
        .option('-r, --replace','If given, then replace files already generaåted, \n\t\totherwise not; optional')
        .parse(process.argv);
}<|endoftext|>


<commit_before>exports.BattleFormatsData = {
  luxray: {
  	inherit: true,
		tier: 'RU'
	},
	jynx: {
		inherit: true,
		tier: 'NU'
	},
	gothitelle: {
		inherit: true,
		tier: 'OU'
	},
	archeops: {
		inherit: true,
		tier: 'OU'
	},
	Wweavile: {
		inherit: true,
		tier: 'OU'
	},
	politoed: {
		inherit: true,
		tier: 'UU'
	},
	ninetales: {
		inherit: true,
		tier: 'UU'
	}
};
<commit_msg>Update format-data.js<commit_after>exports.BattleFormatsData = {
  luxray: {
  	inherit: true,
		tier: 'RU'
	},
	jynx: {
		inherit: true,
		tier: 'NU'
	},
	gothitelle: {
		inherit: true,
		tier: 'OU'
	},
	archeops: {
		inherit: true,
		tier: 'OU'
	},
	Wweavile: {
		inherit: true,
		tier: 'OU'
	},
	politoed: {
		inherit: true,
		tier: 'UU'
	},
	ninetales: {
		inherit: true,
		tier: 'UU'
	},
	darmanitan: {
		inherit: true,
		tier: 'RU'
	},
	gligar: {
		inherit: true,
		tier: 'RU'
	},
	roselia: {
		inherit: true,
		tier: 'RU'
	},
	staraptor: {
		inherit: true,
		tier: 'UU'
	},
	gliscor: {
		inherit: true,
		tier: 'UU'
	},
};
<|endoftext|>