(* Given the sender's XYZ coordinate (sx,sy,sz), and the XYZ coordinate (cx,cy,cz) of a computing element that has the message, test my algorithm for selecting which computing elements to give the message to, such that each computing element within the sphere of radius sendDistance that is centered on the sender's computing element, is given the message exactly once. *)
(* written July 2017 by Kurt Johmann *)
(* Define the width of the cube as an odd positive integer. The sender's XYZ will be placed at the center of this cube and the sendDistance will be set to Floor[cubeWidth/2]. The end result of these settings is that, in effect, the message sphere will just fit into the cube. *)
(* Note: a cubeWidth of 173 is the largest cube size I tried, and it took 45 minutes of CPU time on my 2015 Mac-Mini computer. For comparison, a cubeWidth of 33 took 1 second of CPU time, a cubeWidth of 57 took 9 seconds of CPU time, a cubeWidth of 77 took 30 seconds of CPU time, and a cubeWidth of 123 took 276 seconds (4 minutes and 36 seconds) of CPU time. *)
cubeWidth = 31;
Print["Begin -- The cubeWidth is ", cubeWidth];
bgnTime = TimeUsed[];
If[Not[IntegerQ[cubeWidth]] || EvenQ[cubeWidth] || cubeWidth < 1, Print["Error: cubeWidth is ", cubeWidth, "\ncubeWidth must be a positive, odd integer. The test results will have errors."], Null];
If[cubeWidth > 50, Print["Caution: the larger the cubeWidth, which is ", cubeWidth,", the more time this test will take to finish."], Null];
On[Assert];
Assert[IntegerQ[cubeWidth] && OddQ[cubeWidth] && Positive[cubeWidth]];
(* define givenCnt and initialize to 0 *)
Array[ givenCnt, {cubeWidth,cubeWidth,cubeWidth} ];
Print["Wait ..."];
For[i = 1, i <= cubeWidth, ++i, For[j = 1, j <= cubeWidth, ++j, For[k = 1, k <= cubeWidth, ++k, givenCnt[i][j][k] = 0]]];
(* the sender of the message: set to the sender's XYZ coordinate *)
sx = Ceiling[cubeWidth/2];
sy = Ceiling[cubeWidth/2];
sz = Ceiling[cubeWidth/2];
Print["Sender's XYZ is (", sx, ", ", sy, ", ", sz, ")"];
(* set the sendDistance for this message *)
sendDistance = Floor[cubeWidth/2];
(* Include the sender in the givenCnt since the message originates from the sender CE (CE denotes a computing element). *)
givenCnt[sx][sy][sz] = 1;
(* Define giveTo and initialize to the coordinates of the six adjacent CEs that the sender CE gives the message to at the start of the message-transmission process. *)
giveTo = List[];
(* Note: the givenCnt for each of these six is incremented in the main For loop. *)
AppendTo[ giveTo, {sx + 1,sy,sz}];
AppendTo[ giveTo, {sx - 1,sy,sz}];
AppendTo[ giveTo, {sx,sy + 1,sz}];
AppendTo[ giveTo, {sx,sy - 1,sz}];
AppendTo[ giveTo, {sx,sy,sz + 1}];
AppendTo[ giveTo, {sx,sy,sz - 1}];
Print["First element in giveTo is: ", First[giveTo]];
Assert[Length[giveTo] == 6];
Print["Wait ..."];
(* Test my selection code which decides which CEs the current CE should give the message to. *)
For[tooFarCnt = 0; loopCnt = 0, Length[giveTo] > 0, ++loopCnt,
(* The body of the For statement follows: *)
(* Extract (remove) the first element in the giveTo list. *)
xyz = First[giveTo];
giveTo = Drop[giveTo,1];
(* Ready this XYZ coordinate for examination. *)
cx = Extract[xyz,{1}];
cy = Extract[xyz,{2}];
cz = Extract[xyz,{3}];
(* Reject this XYZ coordinate if it is beyond the allowed sendDistance. *)
distance = Round[Sqrt[(cx - sx)^2 + (cy - sy)^2 + (cz - sz)^2]];
(* Note: In effect, the Continue[] in this If statement discards this XYZ coordinate because it is too far away from the sender CE. *)
If[distance > sendDistance, ++tooFarCnt; Continue[], Null];
(* At this point in this code, the XYZ coordinate is within the allowed distance from the sender, and, in effect, one can assume that the message is given to the CE at this XYZ coordinate and now that CE needs to use the selection code given in my book to determine which adjacent computing elements to give the message to. Note that the distance check in this test code is done above, instead of as a part of the selection code shown below. *)
(* Increment the givenCnt, and report an error if it's > 1. *)
++givenCnt[cx][cy][cz];
If[givenCnt[cx][cy][cz] > 1, Print["Error: more that one give to CE at XYZ (", cx, ",", cy, ",", cz, ").\nERROR, ERROR, ERROR -- ending main For loop.\n"]; Break[], Null];
(* For the CE at (cx, cy, cz), select (determine) which adjacent CEs it should give the message to. Note: being within sendDistance for the selected CEs is done later, above. *)
(* Extend along the X axis. *)
If[cy == sy && cz == sz,
Assert[cx != sx]; (* This computing element is not the sender, hence this Assert. *)
If[cx > sx, AppendTo[giveTo, {cx + 1, cy, cz}], AppendTo[giveTo, {cx - 1, cy, cz}]];
AppendTo[giveTo, {cx, cy + 1, cz}];
AppendTo[giveTo, {cx, cy - 1, cz}];
AppendTo[giveTo, {cx, cy, cz + 1}];
AppendTo[giveTo, {cx, cy, cz - 1}];
Continue[], Null];
(* Fill out the circle that is one computing-element wide, and this circle is perpendicular to the X axis, and this circle's center is at XYZ coordinate (cx, sy, sz). *)
Which[
cz == sz,
Assert[cy != sy];
If[cy > sy, AppendTo[giveTo, {cx, cy + 1, cz}], AppendTo[giveTo, {cx, cy - 1, cz}]];
AppendTo[giveTo, {cx, cy, cz + 1}];
AppendTo[giveTo, {cx, cy, cz - 1}],
cz > sz, AppendTo[giveTo, {cx, cy, cz + 1}],
cz < sz, AppendTo[giveTo, {cx, cy, cz - 1}],
(* One of the above three tests -- cz equals sz, cz > sz, and cz < sz -- should be True, so fail if not. *)
True, Print["failure at Which[]"];Break[]];
] (* end the For *)
Print["The main For loop has finished. The total \"current Wolfram System session\" CPU time used since the above \"Begin\" is: ", TimeUsed[] - bgnTime, " seconds."];
Print["Wait ..."];
totalOfferedOnce = 0;
notContiguousCnt = 0;
unbalancedZerosCnt = 0;
For[i = 1, i <= cubeWidth, ++i, For[j = 1, j <= cubeWidth, ++j,
For[k = 1; onesCnt = 0; firstZerosCnt = 0; secondZerosCnt = 0; haveOnes = False; haveZerosAfterOnes = False; onesNotContiguous = False, k <= cubeWidth, ++k,
If[givenCnt[i][j][k] == 1,
If[haveZerosAfterOnes, onesNotContiguous = True, Null];
++onesCnt;
haveOnes = True;
++totalOfferedOnce,
Assert[givenCnt[i][j][k] == 0];
(* givenCnt[i][j][k] is 0 *)
If[haveOnes, haveZerosAfterOnes = True; ++secondZerosCnt, ++firstZerosCnt]]];
(* check the results *)
If[onesNotContiguous,
++notContiguousCnt;
Print["Error: have a gap in the computing elements given to, at\n(", i, ", ", j, ", [the line from 1 thru cubeWidth inclusive]). onesCnt is: ", onesCnt], Null];
If[haveOnes && firstZerosCnt != secondZerosCnt,
++unbalancedZerosCnt;
Print["Error: have ", firstZerosCnt, " leading zeros, and ", secondZerosCnt, " trailing zeros, at\n(", i, ", ", j, ", [the line from 1 thru cubeWidth inclusive]). They should be equal."]]]];
volumeOfTheSphere = (4 * Pi)/3((cubeWidth / 2)^3);
Print["Finished. Show results:\n\nloopCnt is: ", loopCnt, "\ntooFarCnt count is: ", tooFarCnt,
"\n\nnotContiguousCnt is: ", notContiguousCnt, " (if not 0, this is an error)",
"\nunbalancedZerosCnt is: ", unbalancedZerosCnt, " (if not 0, this is an error)\n",
"\ntotalOfferedOnce: ", totalOfferedOnce, " which should be close to the volume of a sphere whose radius is ", cubeWidth / 2 //N, ", which is ", volumeOfTheSphere //N, " In general, the larger the cubeWidth, the closer the ratio of (totalOfferedOnce / volumeOfTheSphere) will be to 1. This ratio is: ", totalOfferedOnce / volumeOfTheSphere //N];
Assert[notContiguousCnt == 0 && unbalancedZerosCnt == 0];
Print["\nEnd -- The total \"current Wolfram System session\" CPU time used since the above \"Begin\" is: ",TimeUsed[] - bgnTime, " seconds."];