<Cfinclude template="udflib.cfm" />

<cfoutput>
<html>
<head>
<title>Earth/Sun</title>
<style type="text/css">
pre {
	border: 1px dashed ##999;
	background-color: ##f7f7f8;
	margin: 10px 0px;
	padding: 5px 1%;
	clear: both;
}
div.row {
	clear: both;
	padding: 0px;
	margin: 0px;
}
pre.half {
	clear: none;
	float: left;
	width: 47%;
}
pre.half:last-child {
	float: right;
}
pre.third {
	clear: none;
	float: left;
	width: 30%;
}
pre.third:first-child {
	float: left;
	margin-right: 1.5%;
	width: 30%;
}
pre.third:last-child {
	float: right;
	margin-left: 1.5%;
	width: 30%;
}
table.pre th,
table.pre td {
	font-family: monospace;
	white-space: pre;
}
</style>
<link rel="stylesheet" href="/s/table_data.css" type="text/css" />
</head>
<body>
<cfscript>
startTime = getTickCount();
attributes.restart = structKeyExists(attributes, "restart") AND isBoolean(attributes.restart) AND attributes.restart;
attributes.iterate = structKeyExists(attributes, "iterate") AND isBoolean(attributes.iterate) AND attributes.iterate;

variables.my.dsn = "sun_earth";
CHART_INTERVAL = 60; // s (don't change this)
BUILD_INTERVAL = 3 * 60 * 60; // s

Me = 5.974 * 10 ^ 24; // kg
Ms = 1.989 * 10 ^ 30; // kg
r = 1.496 * 10 ^ 11; // m
G = 6.673 * 10 ^ -11; // m^3 * k^-1 * s^-2 == m^3 / (kg * s^2)
</cfscript>
<cffunction name="cache">
	<cfargument name="t" />
	<cfargument name="Det" />
	<cfargument name="Dst" />
	<cfset var Vet = V_e(t) />
	<cfset var Vst = V_s(t) />
	<cfset var Aet = A_e(t) />
	<cfset var Ast = A_s(t) />
	<cfset var Dt = Dst + Det />
	<cfset var rt = r - Dt />
	<cfset var Ft = F(Me, Ms, rt) />
	<cfset var AetF = a(Ft, Me) />
	<cfset var AstF = a(Ft, Ms) />
	<cfquery datasource="#variables.my.dsn#" name="">
		insert delayed into point
			(t, Dt, rt, Ft, AetF, AstF,
			 Det, Dst, Vet, Vst, Aet, Ast
			)
		values
			(<cfqueryparam cfsqltype="cf_sql_integer" value="#t#" />,
			 <cfqueryparam cfsqltype="cf_sql_double" value="#Dt#" />,
			 <cfqueryparam cfsqltype="cf_sql_double" value="#rt#" />,
			 <cfqueryparam cfsqltype="cf_sql_double" value="#Ft#" />,
			 <cfqueryparam cfsqltype="cf_sql_double" value="#AetF#" />,
			 <cfqueryparam cfsqltype="cf_sql_double" value="#AstF#" />,
			 <cfqueryparam cfsqltype="cf_sql_double" value="#Det#" />,
			 <cfqueryparam cfsqltype="cf_sql_double" value="#Dst#" />,
			 <cfqueryparam cfsqltype="cf_sql_double" value="#Vet#" />,
			 <cfqueryparam cfsqltype="cf_sql_double" value="#Vst#" />,
			 <cfif t EQ 0>
			 	<!--- we can't actually derive from distance these at t = 0, so we'll use the force-derived ones. --->  
				<cfqueryparam cfsqltype="cf_sql_double" value="#AetF#" />,
				<cfqueryparam cfsqltype="cf_sql_double" value="#AstF#" />
			 <cfelse>
				<cfqueryparam cfsqltype="cf_sql_double" value="#Aet#" />,
				<cfqueryparam cfsqltype="cf_sql_double" value="#Ast#" />
			 </cfif>
			)
	</cfquery>
</cffunction>
<cfset D_e_cache = {} />
<cfset D_s_cache = {} />
<cfif attributes.restart>
	<cfquery datasource="#variables.my.dsn#" name="">
		delete from point
	</cfquery>
	<cfset cache(0, 0, 0) />
	<cfset lastT = 0 />
<cfelse>
	<cfquery datasource="#variables.my.dsn#" name="lastT">
		select max(t) t
		from point
	</cfquery>
	<cfset lastT = lastT.t /><!--- s --->
</cfif>

<!--- locked! --->
<cfset attributes.t = 5486400 /><!--- 63.5 days / ~88.8% --->
<cfset attributes.showCharts = true />

<cfset maxT = lastT + BUILD_INTERVAL /><!--- s --->
<cfif NOT structKeyExists(attributes, "t")>
	<cfif structKeyExists(attributes, "d") AND isNumeric(attributes.d) AND attributes.d GTE 0 AND attributes.d LTE BUILD_INTERVAL>
		<cfset attributes.t = lastT + attributes.d />
	<cfelse>
		<cfset attributes.t = maxT />
	</cfif>
<cfelseif attributes.t GT maxT>
	<p>Your supplied 't' value was greater than the maximum allowed (#numberFormat(maxT, ',')#), so it was lowered to that.
	</p>
	<cfset attributes.t = maxT />
</cfif>
<cfquery datasource="#variables.my.dsn#" name="get">
	(
		select t, Det, Dst
		from point
		where t <= <cfqueryparam cfsqltype="cf_sql_integer" value="#attributes.t#" />
		order by t desc
		limit 10
	) union distinct (
		select t, Det, Dst
		from point
		order by t desc
		limit 10
	)
</cfquery>
<cfloop query="get">
	<cfset D_e_cache[t] = Det />
	<cfset D_s_cache[t] = Dst />
</cfloop>

<cfscript>
function F(m1, m2, r) {
	return G * m1 * m2 / r ^ 2; // kg * m  / s^2
}
function a(F, m) {
	return F / m; // m / s^2
}
D_e_localCache = {
	0 = 0
};
D_s_localCache = duplicate(D_e_localCache);
structAppend(D_e_localCache, D_e_cache);
structAppend(D_s_localCache, D_s_cache);
function V_e(t) {
	return V_x(D_e, t);
}
function V_s(t) {
	return V_x(D_s, t);
}
function V_x(d, t) {
	if (t LT 1) {
		return 0;
	}
	return d(t) - d(t - 1); // m / s
}
function A_e(t) {
	return A_x(V_e, t);
}
function A_s(t) {
	return A_x(V_s, t);
}
function A_x(v, t) {
	if (t LT 2) {
		return 0;
	}
	return (v(t) - v(t - 1)); // m / s^2
}
function D_e(t) {
	return D_x(t, Me, D_e_cache, D_e_localCache);
}
function D_s(t) {
	return D_x(t, Ms, D_s_cache, D_s_localCache);
}
function D_x(t, M, cache, localCache) {
	if (t LTE 0) {
		return 0;
	} else if (structKeyExists(localCache, t)) {
		return localCache[t];
	} else {
		build_D_x(t);
		return localCache[t];
	}
}
function maxLte(s, v) {
	var result = 0;
	for (i in s) {
		if (i GT result AND i LTE v) {
			result = i;
		}
	}
	return result;
}
function build_D_x(t) {
	var re = 0;
	var rs = 0;
	var n = 1 + min(
		maxLte(D_e_localCache, t),
		maxLte(D_s_localCache, t)
	);
	var lF = 0;
	var De1 = 0;
	var Ds1 = 0;
	writeOutput("<pre>build_D_x(#t#) w/ n = #n#</pre>");
	for (; n LTE t; n += 1) {
		De1 = D_e(n - 1);
		Ds1 = D_s(n - 1);
		lF = F(Me, Ms, r - De1 - Ds1);
		re = De1 + V_e(n - 1) + a(lF, Me); // m
		rs = Ds1 + V_s(n - 1) + a(lF, Ms) ; // m
		D_e_localCache[n] = re;
		D_s_localCache[n] = rs;
		if (n MOD CHART_INTERVAL EQ 0) {
			// cache the magic intervals
			cache(n - 1, D_e_localCache[n - 1], D_s_localCache[n - 1]);
			cache(n, re, rs);
		}
	}
	cache(t - 1, D_e_localCache[t - 1], D_s_localCache[t - 1]);
	cache(t, D_e_localCache[t], D_s_localCache[t]);
}
function log2(n) {
	// log2(x) = log2(10) * log10(x) = log10(x) / log10(2)
	return log10(n) / log10(2);
}
function n(n) {
	if (n LT 10000 AND n GTE 1000) {
		return numberFormat(n, "9999");
	} else if (n LT 1000 AND n GTE 100) {
		return numberFormat(n, "999.0");
	} else if (n LT 100 AND n GTE 10) {
		return numberFormat(n, "99.00");
	} else if (n LT 10 AND n GTE 1) {
		return numberFormat(n, "0.000");
	} else if (n LT 1 AND n GTE 0.1) {
		return numberFormat(n, "0.0000");
	} else if (n LT 0.1 AND n GTE 0.01) {
		return numberFormat(n, "0.00000");
	} else if (n LT 0.01 AND n GTE 0.001) {
		return numberFormat(n, "0.000000");
	}
	return REReplace(scientificFormat(n, 4), 'E(-?[0-9]+)', ' x 10<sup>\1</sup>');
}
function scientificFormat(n,m) {
    return createObject("java","java.text.DecimalFormat").init("0.#repeatString("0", m - 1)#E0").format(createObject("java","java.math.BigDecimal").init(n));
}
function printLookup(c) {
	var a = listToArray(structKeyList(c));
	var i = 0;
	var l = arrayLen(a);
	var ml = 0;
	arraySort(a, 'numeric');
	ml = len(a[l]);
	for (i = 1; i LTE l; i++) {
		writeOutput("#rJustify(a[i], ml)#=#c[a[i]]##chr(10)#");
	}
}
</cfscript>

<p>What if the earth suddenly stopped orbiting the sun?  The gravitational pull would
suddenly no longer have the earth's inertia to counteract it, and the earth would
plummet into the sun.  But how fast?  How long would it take?
</p>

<p>What if both bodies were point masses (i.e., had no volume), there was no
other motion, and were no other bodies around?  Now THAT, I can approximate.  :)
</p>

<p>These are the results of that model, evaluated each second, starting at <code style="white-space:nowrap;">t = 0</code>: 
</p>
<div class="row">
<pre class="third">
Names:
------
t = time
r = Earth-Sun separation (orbital radius)
F = gravitational force
a = acceleration due to gravity
D = distance covered
V = velocity (based on &##916;distance)
A = acceleration (based on &##916;velocity)
</pre>
<pre class="third">
Formulae:
---------

F(M<sub>1</sub>, M<sub>2</sub>, r) = G * M<sub>1</sub> * M<sub>2</sub> / r<sup>2</sup>
a(M, r) = F(M, M<sub>other</sub>, r) / M
D(t) = D(t - 1) + V(t - 1) + A(t - 1)
V(t) = D(t) - D(t - 1)
A(t) = V(t) - V(t - 1)
</pre>
<cfset F0 = F(Me, ms, r) />
<pre class="third">
Constants:
----------
M<sub>e</sub> = Mass<sub>earth</sub> = #n(Me)# kg
M<sub>s</sub> = Mass<sub>sun</sub> = #n(Ms)# kg
r(0) = Earth's mean orbital radius = #n(r)# m
G = Gravitational constant = #n(G)# m<sup>3</sup> / (kg * s<sup>2</sup>)
</pre>
</div>

<cfloop list="0,#attributes.t#,#lastT#" index="t">
	<cfset Det = D_e(t) />
	<cfset Dst = D_s(t) />
	<cfset Vet = V_e(t) />
	<cfset Vst = V_s(t) />
	<cfset Aet = A_e(t) />
	<cfset Ast = A_s(t) />
	<cfset Dt = Dst + Det />
	<cfset rt = r - Dt />
	<cfset Ft = F(Me, Ms, rt) />
	<cfset AetF = a(Ft, Me) />
	<cfset AstF = a(Ft, Ms) />
	<div class="row">
	<cfset s = 't = #numberFormat(t, ",")# s (#round(t / 86400 * 100) / 100# days)' />
	<pre class="third">
#s##chr(10)##repeatString("-", len(s))#
D(t) = #n(Dt)# m (#n(Dt / r * 100)#%)
r(t) = #n(rt)# m<cfif Vet + Vst Gt 0> (&lt; #decimalTimespanFormat(rt / (Vet + Vst))#)</cfif>
F(t) = #n(Ft)# kg * m / s<sup>2</sup>
	</pre>
	<pre class="third">
a<sub>e</sub>(t) = #n(AetF)# m / s<sup>2</sup>
D<sub>e</sub>(t) = #n(Det)# m
V<sub>e</sub>(t) = #n(Vet)# m / s
A<sub>e</sub>(t) = #n(Aet)# m / s<sup>2</sup>
	</pre>
	<pre class="third">
a<sub>s</sub>(t) = #n(AstF)# m / s<sup>2</sup>
D<sub>s</sub>(t) = #n(Dst)# m
V<sub>s</sub>(t) = #n(Vst)# m / s
A<sub>s</sub>(t) = #n(Ast)# m / s<sup>2</sup>
	</pre>
	</div>
</cfloop>
<pre>#numberFormat(getTickCount() - startTime, ",")# ms</pre>
<cfif attributes.iterate>
	<cfif rt GTE 0.1 * r>
		<meta http-equiv="refresh" content="1" />
	<cfelse>
		<cfset attributes.showCharts = true />
	</cfif>
</cfif>
<cfset startTime = getTickCount() />

<cfif NOT structKeyExists(attributes, "showCharts") OR NOT isBoolean(attributes.showCharts) OR NOT attributes.showCharts>
	<p>Add '<code><a href="?showCharts=true">?showCharts=true</a></code>' to see some charts of the progress
	</p>
	<cfexit method="exittemplate" />
</cfif>
<div id="loading">Loading charts...</div>
<cfflush />
<script type="text/javascript">try { var el = document.getElementById("loading");el.parentNode.removeChild(el); } catch (e) { /* oh well */ }</script>

<!--- lets build some charts! --->
<style type="text/css">
.charts img {
	border: 1px solid ##ddd;
	margin: 5px;
	padding: 5px;
}
</style>
<cfset width = 600 />
<cfset height = 200 />
<cfset encoding = "e" />
<cfset Ce = "0099ff" />
<cfset Cs = "ffee00" />
<cfset CF = "cc6600" />
<cfquery datasource="#variables.my.dsn#" name="get">
	select max(t) maxT
	from point
	where t <= <cfqueryparam cfsqltype="cf_sql_integer" value="#attributes.t#" />
</cfquery>
<cfif get.recordCount EQ 0 OR NOT isNumeric(get.maxT)>
	<p>There isn't enough data to render charts yet...
	</p>
	<cfexit method="exittemplate" />
</cfif>
<p>Charts are plotted through #numberFormat(attributes.t, ",")# sec (#attributes.t / 86400# days).  Gravity goes insane after that.  Be careful of y-axis units; they're inconsistent (for different scales), and some are unusual (e.g., gigameters).
</p>
<cfset interval = ceiling(get.maxT / 40 / CHART_INTERVAL) * CHART_INTERVAL />
<cfquery datasource="#variables.my.dsn#" name="get">
	select t as tt, t / 86400 as t, -- hours
		Dt / 1000 / 1000 / 1000 as Dt, -- gigameters
		rt / 1000 / 1000 / 1000 as rt, -- gigameters
		(#r# - Det) / 1000 / 1000 / 1000 as Pet, -- gigameters
		Dst / 1000 / 1000 / 1000 as Pst, -- gigameters
		Ft / 1000 / 1000 / 1000 / 1000 / 1000 / 1000 / 1000 Ft, -- zettanewtons
		Det / 1000 / 1000 / 1000 as Det, -- gigameters
		Dst / 1000 as Dst, -- kilometers
		Vet / 1000 as Vet, -- kilometers / second
		Vst * 100 as Vst, -- centimeters / second
		Aet as Aet, -- meters / second
		Ast * 1000 * 1000 as Ast -- micrometers / second
	from point
	where (t < <cfqueryparam cfsqltype="cf_sql_integer" value="#get.maxT#" />
			and t MOD <cfqueryparam cfsqltype="cf_sql_integer" value="#interval#" /> = 0
		)
		or t = <cfqueryparam cfsqltype="cf_sql_integer" value="#get.maxT#" />
	order by t
</cfquery>
<cffunction name="chart">
	<cfargument name="labels" />
	<cfargument name="series" />
	<cfargument name="sharedAxis" default="false" />
	<cfargument name="colors" default="#Ce#,#Cs#" />
	<cfset var i = 0 />
	<cfset var temp = "" />
	<cfif isSimpleValue(labels)>
		<cfset labels = listToArray(labels, "|") />
	</cfif>
	<cfif isSimpleValue(series)>
		<cfset series = listToArray(series, ",") />
	</cfif>
	<cfif isSimpleValue(colors)>
		<cfset colors = listToArray(colors) />
	</cfif>
	<cfloop from="1" to="#arrayLen(series)#" index="i">
		<cfset temp = series[i] />
		<cfset series[i] = {
			name = temp,
			label = labels[i],
			color = colors[(i - 1) MOD arrayLen(colors) + 1]
		} />
	</cfloop>
	<cfif sharedAxis>
		<cfset temp = {
			min = 0,
			max = 0
		} />
		<cfloop from="1" to="#arrayLen(series)#" index="i">
			<cfset temp.min = min(temp.min, arrayMin(valueArray("get.#series[i].name#"))) />
			<cfset temp.max = max(temp.max, arrayMax(valueArray("get.#series[i].name#"))) />
		</cfloop>
		<cfset temp = getAxisBounds(temp.min, temp.max) />
		<cfloop from="1" to="#arrayLen(series)#" index="i">
			<cfset series[i].bounds = temp />
		</cfloop>
	<cfelse>
		<cfloop from="1" to="#arrayLen(series)#" index="i">
			<cfset series[i].bounds = getAxisBounds(arrayMin(valueArray("get.#series[i].name#")), arrayMax(valueArray("get.#series[i].name#"))) />
		</cfloop>
	</cfif>
	<cfsavecontent variable="chartUrl">
		http://chart.apis.google.com/chart
		?cht=lc
		&chs=#width#x#height#
		&chd=#encoding#:
			<cfloop from="1" to="#arrayLen(series)#" index="i">
				<cfif i GT 1>,</cfif>
				#chartEncode(valueList("get.#series[i].name#"), series[i].bounds.lower, series[i].bounds.upper, encoding)#
			</cfloop>
		&chco=<cfloop from="1" to="#arrayLen(series)#" index="i"><cfif i GT 1>,</cfif>#series[i].color#</cfloop>
		&chm=<cfloop from="1" to="#arrayLen(series)#" index="i"><cfif i GT 1>|</cfif>d,#series[i].color#,#i - 1#,-1,7</cfloop>
		&chdl=<cfloop from="1" to="#arrayLen(series)#" index="i"><cfif i GT 1>|</cfif>#urlEncodedFormat(series[i].label)#</cfloop>
		&chdlp=t
		&chxt=x,x<cfif sharedAxis>,r<cfelse>#repeatString(",r", arrayLen(series))#</cfif>
		&chxl=0:<cfloop query="get">|<cfif currentRow MOD 4 EQ 1>#numberFormat(t, ".0")#</cfif></cfloop>
			|1:|time+(days)
			<cfloop from="1" to="#arrayLen(series)#" index="i">
				|#1 + i#:<cfloop from="#series[i].bounds.lower#" to="#series[i].bounds.upper#" index="i" step="#series[i].bounds.interval * iif(series[i].bounds.intervalCount GT 7, 2, 1)#">|#i#</cfloop>
				<cfif sharedAxis>
					<cfbreak />
				</cfif>
			</cfloop>
		&chxp=1,50
		<cfif arrayLen(series) GT 1 AND NOT sharedAxis>
			<!--- colored axis labels --->
			&chxs=
				<cfloop from="1" to="#arrayLen(series)#" index="i">
					<cfif i GT 1>|</cfif>
					#1 + i#,#series[i].color#
				</cfloop>
		<cfelse>
			&chg=20,#int(100 / (series[1].bounds.intervalCount / iif(series[1].bounds.intervalCount GT 7, 2, 1)) * 100) / 100#
		</cfif>
	</cfsavecontent>
	<cfset chartUrl = REReplace(chartUrl, "[[:space:]]+", "", "all") />
	<!---
	<pre>#len(chartUrl)# chars#chr(10)##replace(chartUrl, "&", "#chr(10)#  &", "all")#</pre>
	--->
	<img src="#chartUrl#" />
</cffunction>
<div class="charts">
<cfset chart("De(t) (Gm)|Ds(t) (km)", "Det,Dst", true) />
<cfset chart("Pe(t) (Gm)|Ps(t) (Gm)", "Pet,Pst", true) />
<cfset chart("Ve(t) (km / s)|Vs(t) (cm / s)", "Vet,Vst", true) />
<cfset chart("F(t) (ZN)", "Ft", false, CF) />
<cfset chart("Ae(t) (m / s^2)|As(t) (μm / s^2)", "Aet,Ast", true) />
</div>

<pre>#numberFormat(getTickCount() - startTime, ",")# ms</pre>

<a href="sun_earth_code.cfm">View the code (masochists only)</a>
</body>
</html>
</cfoutput>