[
  {
    "path": ".gitignore",
    "content": "sunsky\n"
  },
  {
    "path": "HosekCubic.h",
    "content": "#if defined(__clang__)\n    #pragma clang diagnostic push\n    #pragma clang diagnostic ignored \"-Wmissing-braces\"\n#endif\n#ifdef _MSC_VER\n    // double->float\n    #pragma warning(disable : 4305)\n#endif\n\nconst float kHCX[10][4][2][4] =\n{\n    1.471043, 1.471043, 1.471043, 0.758034,     1.572623, 1.572623, 1.572623, 0.8574294,\n    0.06211483, 0.06211483, 0.06211483, -14.77757,     -1.849289, -1.849289, -1.849289, -17.94017,\n    13.80912, 13.80912, 13.80912, 33.88272,     16.35536, 16.35536, 16.35536, 36.58662,\n    6.924387, 6.924387, 6.924387, 19.01121,     17.69317, 17.69317, 17.69317, 30.42875,\n\n    -1.129483, -1.129483, -1.129483, -1.84131,     -1.131649, -1.131649, -1.131649, -1.877993,\n    -1.146944, -1.146944, -1.146944, -4.513189,     -1.138856, -1.138856, -1.138856, -4.543661,\n    -1.031804, -1.031804, -1.031804, -0.02013159,     -1.026747, -1.026747, -1.026747, 0.05537295,\n    -1.072376, -1.072376, -1.072376, -1.161865,     -1.090789, -1.090789, -1.090789, -1.213788,\n\n    -0.1890619, -0.1890619, -0.1890619, -0.9781779,     -0.1954455, -0.1954455, -0.1954455, -1.025135,\n    -0.1989695, -0.1989695, -0.1989695, -2.169127,     -0.1932576, -0.1932576, -0.1932576, -2.237107,\n    -0.1174885, -0.1174885, -0.1174885, -0.1769265,     -0.1240114, -0.1240114, -0.1240114, -0.1157619,\n    -0.1819365, -0.1819365, -0.1819365, -0.1903353,     -0.1906185, -0.1906185, -0.1906185, -0.2468891,\n\n    -9.065101, -9.065101, -9.065101, -4.610903,     -7.751595, -7.751595, -7.751595, -4.311037,\n    6.90358, 6.90358, 6.90358, -6.342882,     5.907544, 5.907544, 5.907544, -3.683992,\n    -2.278102, -2.278102, -2.278102, 4.859248,     -1.144623, -1.144623, -1.144623, 3.403649,\n    1.951545, 1.951545, 1.951545, -1.057945,     2.379103, 2.379103, 2.379103, 0.2798364,\n\n    9.659923, 9.659923, 9.659923, 4.824662,     8.685861, 8.685861, 8.685861, 4.715016,\n    -1.256832, -1.256832, -1.256832, 11.42627,     -3.26039, -3.26039, -3.26039, 8.88321,\n    -7.431575, -7.431575, -7.431575, -14.91425,     -2.056945, -2.056945, -2.056945, -13.34879,\n    11.91233, 11.91233, 11.91233, 14.1308,     5.730488, 5.730488, 5.730488, 13.84061,\n\n    -0.03607819, -0.03607819, -0.03607819, -0.05100806,     -0.04910871, -0.04910871, -0.04910871, -0.04711631,\n    1.567098, 1.567098, 1.567098, -0.25833,     2.126393, 2.126393, 2.126393, -0.1852951,\n    -2.984605, -2.984605, -2.984605, 1.169926,     -4.319021, -4.319021, -4.319021, 1.010055,\n    -4.890863, -4.890863, -4.890863, -4.39955,     -3.860444, -3.860444, -3.860444, -4.504395,\n\n    0.8314359, 0.8314359, 0.8314359, 0.6463776,     0.8992952, 0.8992952, 0.8992952, 0.6335844,\n    0.6585674, 0.6585674, 0.6585674, 0.6981739,     0.4717289, 0.4717289, 0.4717289, 0.1284183,\n    1.555342, 1.555342, 1.555342, -0.3360626,     1.649114, 1.649114, 1.649114, 0.5380275,\n    0.633864, 0.633864, 0.633864, 0.4671616,     -0.2866762, -0.2866762, -0.2866762, -0.3228472,\n\n    0.08181661, 0.08181661, 0.08181661, -6.377724e-06,     0.04710143, 0.04710143, 0.04710143, -7.665398e-06,\n    0.1454988, 0.1454988, 0.1454988, 0.2645415,     0.1985848, 0.1985848, 0.1985848, 0.2123163,\n    0.1480832, 0.1480832, 0.1480832, -0.004541874,     0.02987276, 0.02987276, 0.02987276, 0.09329225,\n    0.1417575, 0.1417575, 0.1417575, 0.4694801,     0.09714939, 0.09714939, 0.09714939, 0.2526912,\n\n    4.768868, 4.768868, 4.768868, 2.216875,     4.254818, 4.254818, 4.254818, 1.788017,\n    3.128158, 3.128158, 3.128158, 3.146712,     2.910323, 2.910323, 2.910323, 2.880383,\n    2.114148, 2.114148, 2.114148, -1.956962,     1.846372, 1.846372, 1.846372, -2.190931,\n    2.104099, 2.104099, 2.104099, 1.781559,     1.535069, 1.535069, 1.535069, 1.028129,\n\n    0.6339777, 0.6339777, 0.6339777, 0.861853,     0.6821116, 0.6821116, 0.6821116, 0.9001409,\n    0.6501364, 0.6501364, 0.6501364, 0.2343821,     0.6093358, 0.6093358, 0.6093358, 0.1875418,\n    0.7585136, 0.7585136, 0.7585136, 0.9343759,     0.8037416, 0.8037416, 0.8037416, 0.9764438,\n    0.621865, 0.621865, 0.621865, 0.6298435,     0.6047409, 0.6047409, 0.6047409, 0.6153335,\n\n};\n\nconst float kHCY[10][4][2][4] =\n{\n    1.522034, 1.522034, 1.522034, 0.7985143,     1.63172, 1.63172, 1.63172, 0.903157,\n    0.01937461, 0.01937461, 0.01937461, -15.66538,     -2.025983, -2.025983, -2.025983, -18.97756,\n    15.02212, 15.02212, 15.02212, 35.28313,     17.82944, 17.82944, 17.82944, 38.12075,\n    7.200506, 7.200506, 7.200506, 20.2541,     18.83474, 18.83474, 18.83474, 32.12934,\n\n    -1.144464, -1.144464, -1.144464, -1.896737,     -1.146417, -1.146417, -1.146417, -1.989159,\n    -1.165054, -1.165054, -1.165054, -4.528923,     -1.156448, -1.156448, -1.156448, -4.570587,\n    -1.031311, -1.031311, -1.031311, -0.09059,     -1.023365, -1.023365, -1.023365, -0.02246192,\n    -1.080783, -1.080783, -1.080783, -1.144353,     -1.100497, -1.100497, -1.100497, -1.192592,\n\n    -0.2043799, -0.2043799, -0.2043799, -1.005442,     -0.2119353, -0.2119353, -0.2119353, -1.09516,\n    -0.2004164, -0.2004164, -0.2004164, -2.13232,     -0.1969813, -0.1969813, -0.1969813, -2.206897,\n    -0.1307805, -0.1307805, -0.1307805, -0.2715155,     -0.1283175, -0.1283175, -0.1283175, -0.2106788,\n    -0.1759721, -0.1759721, -0.1759721, -0.1738116,     -0.1921495, -0.1921495, -0.1921495, -0.2334839,\n\n    -10.20188, -10.20188, -10.20188, -6.411032,     -7.187525, -7.187525, -7.187525, -2.91555,\n    7.684557, 7.684557, 7.684557, -7.169267,     5.387022, 5.387022, 5.387022, -4.545533,\n    -2.61168, -2.61168, -2.61168, 5.600186,     -0.7958836, -0.7958836, -0.7958836, 3.865181,\n    2.071102, 2.071102, 2.071102, -1.244332,     2.340057, 2.340057, 2.340057, 0.1678497,\n\n    10.71247, 10.71247, 10.71247, 6.54822,     8.058599, 8.058599, 8.058599, 3.275339,\n    -1.234814, -1.234814, -1.234814, 11.66098,     -2.357146, -2.357146, -2.357146, 10.73598,\n    -8.965322, -8.965322, -8.965322, -14.42182,     -3.07873, -3.07873, -3.07873, -15.61103,\n    14.55553, 14.55553, 14.55553, 12.77613,     5.908181, 5.908181, 5.908181, 15.81022,\n\n    -0.03256693, -0.03256693, -0.03256693, -0.03227596,     -0.05256438, -0.05256438, -0.05256438, -0.05735765,\n    2.449292, 2.449292, 2.449292, -0.6445193,     2.653214, 2.653214, 2.653214, -0.09496659,\n    -4.829063, -4.829063, -4.829063, 1.847776,     -5.56008, -5.56008, -5.56008, 0.8480998,\n    -4.762894, -4.762894, -4.762894, -4.721684,     -3.895817, -3.895817, -3.895817, -4.460663,\n\n    0.7860205, 0.7860205, 0.7860205, 0.5717262,     0.8375733, 0.8375733, 0.8375733, 0.5742174,\n    0.5882703, 0.5882703, 0.5882703, 1.000333,     0.4095826, 0.4095826, 0.4095826, 0.23352,\n    1.657856, 1.657856, 1.657856, -0.744886,     1.714468, 1.714468, 1.714468, 0.4990387,\n    0.6540962, 0.6540962, 0.6540962, 0.8189706,     -0.2843079, -0.2843079, -0.2843079, -0.3637103,\n\n    0.06872719, 0.06872719, 0.06872719, -8.115192e-06,     0.03887093, 0.03887093, 0.03887093, -7.683288e-06,\n    0.1236639, 0.1236639, 0.1236639, 0.2336697,     0.1680872, 0.1680872, 0.1680872, 0.1651105,\n    0.1423908, 0.1423908, 0.1423908, -0.008540288,     0.04715908, 0.04715908, 0.04715908, 0.119553,\n    0.1537134, 0.1537134, 0.1537134, 0.4823636,     0.1017511, 0.1017511, 0.1017511, 0.2322143,\n\n    4.824771, 4.824771, 4.824771, 2.296704,     4.222111, 4.222111, 4.222111,  1.7634,\n    3.988477, 3.988477, 3.988477, 3.374217,     3.698663, 3.698663, 3.698663, 3.098112,\n    1.194806, 1.194806, 1.194806, -2.087303,     1.141016, 1.141016, 1.141016, -2.325808,\n    2.351988, 2.351988, 2.351988, 1.793429,     1.648862, 1.648862, 1.648862, 1.025036,\n\n    0.6259836, 0.6259836, 0.6259836, 0.9000749,     0.6695347, 0.6695347, 0.6695347, 0.9001342,\n      0.661,   0.661,   0.661, 0.2071559,     0.6285347, 0.6285347, 0.6285347, 0.1926854,\n    0.7467407, 0.7467407, 0.7467407, 0.9458723,     0.7764328, 0.7764328, 0.7764328, 0.9730061,\n    0.6239002, 0.6239002, 0.6239002, 0.627587,     0.6200643, 0.6200643, 0.6200643, 0.6151967,\n\n};\n\nconst float kHCZ[10][4][2][4] =\n{\n    1.107408, 1.107408, 1.107408, 0.7623528,     1.286344, 1.286344, 1.286344, 0.8640731,\n    -5.212171, -5.212171, -5.212171, -16.35464,     -8.835278, -8.835278, -8.835278, -18.67052,\n    22.17711, 22.17711, 22.17711, 32.60661,     25.82934, 25.82934, 25.82934, 33.59812,\n    13.87383, 13.87383, 13.87383, 24.37655,     32.24473, 32.24473, 32.24473, 38.16179,\n\n    -1.353023, -1.353023, -1.353023, -2.728867,     -1.325672, -1.325672, -1.325672, -2.770408,\n    -1.266555, -1.266555, -1.266555, -3.985086,     -1.257786, -1.257786, -1.257786, -4.017327,\n    -0.9608197, -0.9608197, -0.9608197, -0.2876793,     -0.9304037, -0.9304037, -0.9304037, -0.08642912,\n    -1.093978, -1.093978, -1.093978, -1.161188,     -1.106813, -1.106813, -1.106813, -1.147125,\n\n    -0.4813523, -0.4813523, -0.4813523, -1.580388,     -0.4769313, -0.4769313, -0.4769313, -1.618911,\n    -0.3736056, -0.3736056, -0.3736056, -2.138492,     -0.3975163, -0.3975163, -0.3975163, -2.227182,\n    -0.1174827, -0.1174827, -0.1174827, -0.1843867,     -0.07153632, -0.07153632, -0.07153632, -0.1488523,\n    -0.2007022, -0.2007022, -0.2007022, -0.3973276,     -0.2098484, -0.2098484, -0.2098484, -0.2583446,\n\n    -31.0492, -31.0492, -31.0492, -30.79627,     -41.11215, -41.11215, -41.11215, -25.04031,\n    -0.2305174, -0.2305174, -0.2305174, 18.77094,     5.851173, 5.851173, 5.851173, 2.370717,\n    3.809405, 3.809405, 3.809405, -10.44642,     0.9176657, 0.9176657, 0.9176657, 0.8439043,\n    -1.424772, -1.424772, -1.424772, 2.276975,     0.5190111, 0.5190111, 0.5190111, 0.5619681,\n\n    31.40156, 31.40156, 31.40156, 30.92586,     41.68293, 41.68293, 41.68293, 25.31674,\n    0.04713231, 0.04713231, 0.04713231, -18.70684,     -5.894221, -5.894221, -5.894221, -0.9216983,\n    -3.092963, -3.092963, -3.092963, 10.31492,     0.2694842, 0.2694842, 0.2694842, -3.038832,\n    2.058657, 2.058657, 2.058657, -0.5924354,     0.5363316, 0.5363316, 0.5363316, 3.965227,\n\n    -0.009510741, -0.009510741, -0.009510741, -0.004197673,     -0.006274997, -0.006274997, -0.006274997, -0.004239279,\n    -0.1208266, -0.1208266, -0.1208266, -0.2283553,     -0.03763597, -0.03763597, -0.03763597, -0.856935,\n    0.1478871, 0.1478871, 0.1478871, 0.4028665,     -0.007877082, -0.007877082, -0.007877082, 2.010754,\n    0.05271995, 0.05271995, 0.05271995, -1.454436,     0.1698868, 0.1698868, 0.1698868, -3.640249,\n\n    0.554203, 0.554203, 0.554203, 0.3154759,     0.4649469, 0.4649469, 0.4649469, 0.3241013,\n    0.981547, 0.981547, 0.981547, 0.5661614,     0.9146686, 0.9146686, 0.9146686, 0.7383983,\n    -0.06462997, -0.06462997, -0.06462997, 0.1634221,     0.006675631, 0.006675631, 0.006675631, -0.1595713,\n    1.522406, 1.522406, 1.522406, 0.2908837,     0.8347491, 0.8347491, 0.8347491, 0.3629976,\n\n    0.008135471, 0.008135471, 0.008135471, -3.897675e-06,     0.01119411, 0.01119411, 0.01119411, -3.764484e-06,\n    -0.008283749, -0.008283749, -0.008283749, -0.05800496,     -0.00357069, -0.00357069, -0.00357069, -0.05083194,\n    0.110339, 0.110339, 0.110339, 0.1772075,     0.08754471, 0.08754471, 0.08754471, 0.1686509,\n    0.1131037, 0.1131037, 0.1131037, 0.2486994,     0.05040883, 0.05040883, 0.05040883, 0.1849058,\n\n    3.136646, 3.136646, 3.136646, 1.920567,     2.631267, 2.631267, 2.631267, 1.586843,\n    4.22313, 4.22313, 4.22313, 3.329891,     3.320134, 3.320134, 3.320134, 3.273266,\n    1.294505, 1.294505, 1.294505, -0.6514511,     1.608702, 1.608702, 1.608702, -1.662017,\n    3.47811, 3.47811, 3.47811, 1.170225,     3.033226, 3.033226, 3.033226, 1.452241,\n\n    0.5215989, 0.5215989, 0.5215989, 0.6664791,     0.5234546, 0.5234546, 0.5234546, 0.7035906,\n    0.769796, 0.769796, 0.769796, 0.4996695,     0.794625, 0.794625, 0.794625, 0.4611094,\n    0.5914896, 0.5914896, 0.5914896, 0.7820878,     0.5848916, 0.5848916, 0.5848916, 0.8114535,\n    0.6819321, 0.6819321, 0.6819321, 0.6657748,     0.696082, 0.696082, 0.696082, 0.6572598,\n\n};\n\n#if defined(__clang__)\n#pragma clang diagnostic pop\n#endif\n"
  },
  {
    "path": "HosekDataXYZ.h",
    "content": "/*\nThis source is published under the following 3-clause BSD license.\n\nCopyright (c) 2012 - 2013, Lukas Hosek and Alexander Wilkie\nAll rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are met:\n\n    * Redistributions of source code must retain the above copyright\n      notice, this list of conditions and the following disclaimer.\n    * Redistributions in binary form must reproduce the above copyright\n      notice, this list of conditions and the following disclaimer in the\n      documentation and/or other materials provided with the distribution.\n    * None of the names of the contributors may be used to endorse or promote\n      products derived from this software without specific prior written\n      permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\nANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\nWARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\nDISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY\nDIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\nLOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\nON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\nSOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n*/\n\n\n/* ============================================================================\n\nThis file is part of a sample implementation of the analytical skylight and\nsolar radiance models presented in the SIGGRAPH 2012 paper\n\n\n           \"An Analytic Model for Full Spectral Sky-Dome Radiance\"\n\nand the 2013 IEEE CG&A paper\n\n       \"Adding a Solar Radiance Function to the Hosek Skylight Model\"\n\n                                   both by\n\n                       Lukas Hosek and Alexander Wilkie\n                Charles University in Prague, Czech Republic\n\n\n                        Version: 1.4a, February 22nd, 2013\n\nVersion history:\n\n1.4a  February 22nd, 2013\n      Removed unnecessary and counter-intuitive solar radius parameters\n      from the interface of the colourspace sky dome initialisation functions.\n\n1.4   February 11th, 2013\n      Fixed a bug which caused the relative brightness of the solar disc\n      and the sky dome to be off by a factor of about 6. The sun was too\n      bright: this affected both normal and alien sun scenarios. The\n      coefficients of the solar radiance function were changed to fix this.\n\n1.3   January 21st, 2013 (not released to the public)\n      Added support for solar discs that are not exactly the same size as\n      the terrestrial sun. Also added support for suns with a different\n      emission spectrum (\"Alien World\" functionality).\n\n1.2a  December 18th, 2012\n      Fixed a mistake and some inaccuracies in the solar radiance function\n      explanations found in ArHosekSkyModel.h. The actual source code is\n      unchanged compared to version 1.2.\n\n1.2   December 17th, 2012\n      Native RGB data and a solar radiance function that matches the turbidity\n      conditions were added.\n\n1.1   September 2012\n      The coefficients of the spectral model are now scaled so that the output\n      is given in physical units: W / (m^-2 * sr * nm). Also, the output of the\n      XYZ model is now no longer scaled to the range [0...1]. Instead, it is\n      the result of a simple conversion from spectral data via the CIE 2 degree\n      standard observer matching functions. Therefore, after multiplication\n      with 683 lm / W, the Y channel now corresponds to luminance in lm.\n\n1.0   May 11th, 2012\n      Initial release.\n\n\nPlease visit http://cgg.mff.cuni.cz/projects/SkylightModelling/ to check if\nan updated version of this code has been published!\n\n============================================================================ */\n\n\n/*\n\nThis file contains the coefficient data for the XYZ colour space version of\nthe model.\n\n*/\n\n#if defined(__clang__)\n\t#pragma clang diagnostic push\n\t#pragma clang diagnostic ignored \"-Wmissing-braces\"\n#endif\n#ifdef _MSC_VER\n\t// double->float\n\t#pragma warning(disable : 4305)\n#endif\n\n// Uses Sep 9 pattern / Aug 23 mean dataset\n\n// [albedo][turbidity][quinticCoeffs][coeffs]\nfloat kHosekCoeffsX[2][10][6][9] =\n{\n\t// albedo 0, turbidity 1\n\t-1.117001e+000,\n\t-1.867262e-001,\n\t-1.113505e+001,\n\t1.259865e+001,\n\t-3.937339e-002,\n\t1.167571e+000,\n\n\t7.100686e-003,\n\t3.592678e+000,\n\t6.083296e-001,\n\t-1.152006e+000,\n\t-1.926669e-001,\n\t6.152049e+000,\n\n\t-4.770802e+000,\n\t-8.704701e-002,\n\t7.483626e-001,\n\t3.372718e-002,\n\t4.464592e+000,\n\t4.036546e-001,\n\t-1.072371e+000,\n\t-2.696632e-001,\n\t2.816168e-001,\n\t1.820571e+000,\n\t-3.742666e-001,\n\t2.080607e+000,\n\t-7.675295e-002,\n\t-2.835366e+000,\n\t1.129329e+000,\n\t-1.109935e+000,\n\t-1.532764e-001,\n\t1.198787e+000,\n\t-9.015183e-001,\n\t5.173015e-003,\n\t5.749178e-001,\n\t1.075633e-001,\n\t4.387949e+000,\n\t2.650413e-001,\n\t-1.052297e+000,\n\t-2.229452e-001,\n\t1.952347e+000,\n\t5.727205e-001,\n\t-4.885070e+000,\n\t1.984016e+000,\n\t-1.106197e-001,\n\t-4.898361e-001,\n\t8.907873e-001,\n\t-1.070108e+000,\n\t-1.600465e-001,\n\t1.593886e+000,\n\t-4.479251e-005,\n\t-3.306541e+000,\n\t9.390193e-001,\n\t9.513168e-002,\n\t2.343583e+000,\n\t5.335404e-001,\n\t// albedo 0, turbidity 2\n\t-1.113253e+000,\n\t-1.699600e-001,\n\t-1.038822e+001,\n\t1.137513e+001,\n\t-4.040911e-002,\n\t1.037455e+000,\n\t4.991792e-002,\n\t4.801919e+000,\n\t6.302710e-001,\n\t-1.135747e+000,\n\t-1.678594e-001,\n\t4.970755e+000,\n\t-4.430230e+000,\n\t-6.657408e-002,\n\t3.636161e-001,\n\t1.558009e-001,\n\t6.013370e+000,\n\t3.959601e-001,\n\t-1.095892e+000,\n\t-2.732595e-001,\n\t7.666496e-001,\n\t1.350731e+000,\n\t-4.401401e-001,\n\t2.470135e+000,\n\t-1.707929e-001,\n\t-3.260793e+000,\n\t1.170337e+000,\n\t-1.073668e+000,\n\t-2.603929e-002,\n\t-1.944589e-001,\n\t4.575207e-001,\n\t6.878164e-001,\n\t-1.390770e-001,\n\t3.690299e-001,\n\t7.885781e+000,\n\t1.877694e-001,\n\t-1.070091e+000,\n\t-2.798957e-001,\n\t2.338478e+000,\n\t-2.647221e+000,\n\t-7.387808e+000,\n\t2.329210e+000,\n\t-1.644639e-001,\n\t-2.003710e+000,\n\t9.874527e-001,\n\t-1.067120e+000,\n\t-1.418866e-001,\n\t1.254090e+000,\n\t6.053048e+000,\n\t-2.918892e+000,\n\t5.322812e-001,\n\t1.613053e-001,\n\t3.018161e+000,\n\t5.274090e-001,\n\t// albedo 0, turbidity 3\n\t-1.129483e+000,\n\t-1.890619e-001,\n\t-9.065101e+000,\n\t9.659923e+000,\n\t-3.607819e-002,\n\t8.314359e-001,\n\t8.181661e-002,\n\t4.768868e+000,\n\t6.339777e-001,\n\t-1.146420e+000,\n\t-1.883579e-001,\n\t3.309173e+000,\n\t-3.127882e+000,\n\t-6.938176e-002,\n\t3.987113e-001,\n\t1.400581e-001,\n\t6.283042e+000,\n\t5.267076e-001,\n\t-1.128348e+000,\n\t-2.641305e-001,\n\t1.223176e+000,\n\t5.514952e-002,\n\t-3.490649e-001,\n\t1.997784e+000,\n\t-4.123709e-002,\n\t-2.251251e+000,\n\t9.483466e-001,\n\t-1.025820e+000,\n\t1.404690e-002,\n\t-1.187406e+000,\n\t2.729900e+000,\n\t5.877588e-001,\n\t-2.761140e-001,\n\t4.602633e-001,\n\t8.305125e+000,\n\t3.945001e-001,\n\t-1.083957e+000,\n\t-2.606679e-001,\n\t2.207108e+000,\n\t-7.202803e+000,\n\t-5.968103e+000,\n\t2.129455e+000,\n\t-7.789512e-002,\n\t-1.137688e+000,\n\t8.871769e-001,\n\t-1.062465e+000,\n\t-1.512189e-001,\n\t1.042881e+000,\n\t1.427839e+001,\n\t-4.242214e+000,\n\t4.038100e-001,\n\t1.997780e-001,\n\t2.814449e+000,\n\t5.803196e-001,\n\t// albedo 0, turbidity 4\n\t-1.175099e+000,\n\t-2.410789e-001,\n\t-1.108587e+001,\n\t1.133404e+001,\n\t-1.819300e-002,\n\t6.772942e-001,\n\t9.605043e-002,\n\t4.231166e+000,\n\t6.239972e-001,\n\t-1.224207e+000,\n\t-2.883527e-001,\n\t3.002206e+000,\n\t-2.649612e+000,\n\t-4.795418e-002,\n\t4.984398e-001,\n\t3.251434e-002,\n\t4.851611e+000,\n\t6.551019e-001,\n\t-1.136955e+000,\n\t-2.423048e-001,\n\t1.058823e+000,\n\t-2.489236e-001,\n\t-2.462179e-001,\n\t1.933140e+000,\n\t9.106828e-002,\n\t-1.905869e-001,\n\t8.171065e-001,\n\t-1.014535e+000,\n\t-8.262500e-003,\n\t-1.448017e+000,\n\t2.295788e+000,\n\t3.510334e-001,\n\t-1.477418e+000,\n\t5.432449e-001,\n\t5.762796e+000,\n\t4.908751e-001,\n\t-1.070666e+000,\n\t-2.379780e-001,\n\t1.844589e+000,\n\t-5.442448e+000,\n\t-4.012768e+000,\n\t2.945275e+000,\n\t9.854725e-003,\n\t8.455959e-002,\n\t8.145030e-001,\n\t-1.071525e+000,\n\t-1.777132e-001,\n\t8.076590e-001,\n\t9.925865e+000,\n\t-3.324623e+000,\n\t-6.367437e-001,\n\t2.844581e-001,\n\t2.248384e+000,\n\t6.544022e-001,\n\t// albedo 0, turbidity 5\n\t-1.218818e+000,\n\t-2.952382e-001,\n\t-1.345975e+001,\n\t1.347153e+001,\n\t-6.814585e-003,\n\t5.079068e-001,\n\t1.197230e-001,\n\t3.776949e+000,\n\t5.836961e-001,\n\t-1.409868e+000,\n\t-5.114330e-001,\n\t2.776539e+000,\n\t-2.039001e+000,\n\t-2.673769e-002,\n\t4.145288e-001,\n\t7.829342e-004,\n\t2.275883e+000,\n\t6.629691e-001,\n\t-1.069151e+000,\n\t-9.434247e-002,\n\t7.293972e-001,\n\t-1.222473e+000,\n\t-1.533461e-001,\n\t2.160357e+000,\n\t4.626837e-002,\n\t3.852415e+000,\n\t8.593570e-001,\n\t-1.021306e+000,\n\t-1.149551e-001,\n\t-1.108414e+000,\n\t4.178343e+000,\n\t4.013665e-001,\n\t-2.222814e+000,\n\t6.929462e-001,\n\t1.392652e+000,\n\t4.401662e-001,\n\t-1.074251e+000,\n\t-2.224002e-001,\n\t1.372356e+000,\n\t-8.858704e+000,\n\t-3.922660e+000,\n\t3.020018e+000,\n\t-1.458724e-002,\n\t1.511186e+000,\n\t8.288064e-001,\n\t-1.062048e+000,\n\t-1.526582e-001,\n\t4.921067e-001,\n\t1.485522e+001,\n\t-3.229936e+000,\n\t-8.426604e-001,\n\t3.916243e-001,\n\t2.678994e+000,\n\t6.689264e-001,\n\t// albedo 0, turbidity 6\n\t-1.257023e+000,\n\t-3.364700e-001,\n\t-1.527795e+001,\n\t1.504223e+001,\n\t2.717715e-003,\n\t3.029910e-001,\n\t1.636851e-001,\n\t3.561663e+000,\n\t5.283161e-001,\n\t-1.635124e+000,\n\t-7.329993e-001,\n\t3.523939e+000,\n\t-2.566337e+000,\n\t-1.902543e-002,\n\t5.505483e-001,\n\t-6.242176e-002,\n\t1.065992e+000,\n\t6.654236e-001,\n\t-9.295823e-001,\n\t4.845834e-002,\n\t-2.992990e-001,\n\t-2.001327e-001,\n\t-8.019339e-002,\n\t1.807806e+000,\n\t9.020277e-002,\n\t5.095372e+000,\n\t8.639936e-001,\n\t-1.093740e+000,\n\t-2.148608e-001,\n\t-5.216240e-001,\n\t2.119777e+000,\n\t9.506454e-002,\n\t-1.831439e+000,\n\t6.961204e-001,\n\t1.102084e-001,\n\t4.384319e-001,\n\t-1.044181e+000,\n\t-1.849257e-001,\n\t9.071246e-001,\n\t-4.648901e+000,\n\t-2.279385e+000,\n\t2.356502e+000,\n\t-4.169147e-002,\n\t1.932557e+000,\n\t8.296550e-001,\n\t-1.061451e+000,\n\t-1.458745e-001,\n\t2.952267e-001,\n\t8.967214e+000,\n\t-3.726228e+000,\n\t-5.022316e-001,\n\t5.684877e-001,\n\t3.102347e+000,\n\t6.658443e-001,\n\t// albedo 0, turbidity 7\n\t-1.332391e+000,\n\t-4.127769e-001,\n\t-9.328643e+000,\n\t9.046194e+000,\n\t3.457775e-003,\n\t3.377425e-001,\n\t1.530909e-001,\n\t3.301209e+000,\n\t4.997917e-001,\n\t-1.932002e+000,\n\t-9.947777e-001,\n\t-2.042329e+000,\n\t3.586940e+000,\n\t-5.642182e-002,\n\t8.130478e-001,\n\t-8.195988e-002,\n\t1.118294e-001,\n\t5.617231e-001,\n\t-8.707374e-001,\n\t1.286999e-001,\n\t1.820054e+000,\n\t-4.674706e+000,\n\t3.317471e-003,\n\t5.919018e-001,\n\t1.975278e-001,\n\t6.686519e+000,\n\t9.631727e-001,\n\t-1.070378e+000,\n\t-3.030579e-001,\n\t-9.041938e-001,\n\t6.200201e+000,\n\t1.232207e-001,\n\t-3.650628e-001,\n\t5.029403e-001,\n\t-2.903162e+000,\n\t3.811408e-001,\n\t-1.063035e+000,\n\t-1.637545e-001,\n\t5.853072e-001,\n\t-7.889906e+000,\n\t-1.200641e+000,\n\t1.035018e+000,\n\t1.192093e-001,\n\t3.267054e+000,\n\t8.416151e-001,\n\t-1.053655e+000,\n\t-1.562286e-001,\n\t2.423683e-001,\n\t1.128575e+001,\n\t-4.363262e+000,\n\t-7.314160e-002,\n\t5.642088e-001,\n\t2.514023e+000,\n\t6.670457e-001,\n\t// albedo 0, turbidity 8\n\t-1.366112e+000,\n\t-4.718287e-001,\n\t-7.876222e+000,\n\t7.746900e+000,\n\t-9.182309e-003,\n\t4.716076e-001,\n\t8.320252e-002,\n\t3.165603e+000,\n\t5.392334e-001,\n\t-2.468204e+000,\n\t-1.336340e+000,\n\t-5.386723e+000,\n\t7.072672e+000,\n\t-8.329266e-002,\n\t8.636876e-001,\n\t-1.978177e-002,\n\t-1.326218e-001,\n\t2.979222e-001,\n\t-9.653522e-001,\n\t-2.373416e-002,\n\t1.810250e+000,\n\t-6.467262e+000,\n\t1.410706e-001,\n\t-4.753717e-001,\n\t3.003095e-001,\n\t6.551163e+000,\n\t1.151083e+000,\n\t-8.943186e-001,\n\t-2.487152e-001,\n\t-2.308960e-001,\n\t8.512648e+000,\n\t1.298402e-001,\n\t1.034705e+000,\n\t2.303509e-001,\n\t-3.924095e+000,\n\t2.982717e-001,\n\t-1.146999e+000,\n\t-2.318784e-001,\n\t8.992419e-002,\n\t-9.933614e+000,\n\t-8.860920e-001,\n\t-3.071656e-002,\n\t2.852012e-001,\n\t3.046199e+000,\n\t8.599001e-001,\n\t-1.032399e+000,\n\t-1.645145e-001,\n\t2.683599e-001,\n\t1.327701e+001,\n\t-4.407670e+000,\n\t7.709869e-002,\n\t4.951727e-001,\n\t1.957277e+000,\n\t6.630943e-001,\n\t// albedo 0, turbidity 9\n\t-1.469070e+000,\n\t-6.135092e-001,\n\t-6.506263e+000,\n\t6.661315e+000,\n\t-3.835383e-002,\n\t7.150413e-001,\n\t7.784318e-003,\n\t2.820577e+000,\n\t6.756784e-001,\n\t-2.501583e+000,\n\t-1.247404e+000,\n\t-1.523462e+001,\n\t1.633191e+001,\n\t-1.204803e-002,\n\t5.896471e-001,\n\t-2.002023e-002,\n\t1.144647e+000,\n\t6.177874e-002,\n\t-2.438672e+000,\n\t-1.127291e+000,\n\t5.731172e+000,\n\t-1.021350e+001,\n\t6.165610e-002,\n\t-7.752641e-001,\n\t4.708254e-001,\n\t4.176847e+000,\n\t1.200881e+000,\n\t-1.513427e-001,\n\t9.792731e-002,\n\t-1.612349e+000,\n\t9.814289e+000,\n\t5.188921e-002,\n\t1.716403e+000,\n\t-7.039255e-002,\n\t-2.815115e+000,\n\t3.291874e-001,\n\t-1.318511e+000,\n\t-3.650554e-001,\n\t4.221268e-001,\n\t-9.294529e+000,\n\t-4.397520e-002,\n\t-8.100625e-001,\n\t3.742719e-001,\n\t1.834166e+000,\n\t8.223450e-001,\n\t-1.016009e+000,\n\t-1.820264e-001,\n\t1.278426e-001,\n\t1.182696e+001,\n\t-4.801528e+000,\n\t4.947899e-001,\n\t4.660378e-001,\n\t1.601254e+000,\n\t6.702359e-001,\n\t// albedo 0, turbidity 10\n\t-1.841310e+000,\n\t-9.781779e-001,\n\t-4.610903e+000,\n\t4.824662e+000,\n\t-5.100806e-002,\n\t6.463776e-001,\n\t-6.377724e-006,\n\t2.216875e+000,\n\t8.618530e-001,\n\t-2.376373e+000,\n\t-1.108657e+000,\n\t-1.489799e+001,\n\t1.546458e+001,\n\t4.091025e-002,\n\t9.761780e-002,\n\t-1.048958e-002,\n\t2.165834e+000,\n\t-1.609171e-001,\n\t-4.710318e+000,\n\t-2.261963e+000,\n\t6.947327e+000,\n\t-1.034828e+001,\n\t-1.325542e-001,\n\t7.508674e-001,\n\t2.247553e-001,\n\t2.873142e+000,\n\t1.297100e+000,\n\t2.163750e-001,\n\t-1.944345e-001,\n\t-2.437860e+000,\n\t1.011314e+001,\n\t4.450500e-001,\n\t3.111492e-001,\n\t2.751323e-001,\n\t-1.627906e+000,\n\t2.531213e-001,\n\t-1.258794e+000,\n\t-3.524641e-001,\n\t8.425444e-001,\n\t-1.085313e+001,\n\t-1.154381e+000,\n\t-4.638014e-001,\n\t-2.781115e-003,\n\t4.344498e-001,\n\t8.507091e-001,\n\t-1.018938e+000,\n\t-1.804153e-001,\n\t-6.354054e-002,\n\t1.573150e+001,\n\t-4.386999e+000,\n\t6.211115e-001,\n\t5.294648e-001,\n\t1.580749e+000,\n\t6.586655e-001,\n\t// albedo 1, turbidity 1\n\t-1.116416e+000,\n\t-1.917524e-001,\n\t-1.068233e+001,\n\t1.222221e+001,\n\t-3.668978e-002,\n\t1.054022e+000,\n\t1.592132e-002,\n\t3.180583e+000,\n\t5.627370e-001,\n\t-1.132341e+000,\n\t-1.671286e-001,\n\t5.976499e+000,\n\t-4.227366e+000,\n\t-9.542489e-002,\n\t8.664938e-001,\n\t8.351793e-003,\n\t4.876068e+000,\n\t4.492779e-001,\n\t-1.087635e+000,\n\t-3.173679e-001,\n\t4.314407e-001,\n\t1.100555e+000,\n\t-4.410057e-001,\n\t1.677253e+000,\n\t-3.005925e-002,\n\t-4.201249e+000,\n\t1.070902e+000,\n\t-1.083031e+000,\n\t-8.847705e-002,\n\t1.291773e+000,\n\t4.546776e-001,\n\t3.091894e-001,\n\t7.261760e-001,\n\t4.203659e-002,\n\t5.990615e+000,\n\t3.704756e-001,\n\t-1.057899e+000,\n\t-2.246706e-001,\n\t2.329563e+000,\n\t-1.219656e+000,\n\t-5.335260e+000,\n\t8.545378e-001,\n\t-3.906209e-002,\n\t-9.025499e-001,\n\t7.797348e-001,\n\t-1.073305e+000,\n\t-1.522553e-001,\n\t1.767063e+000,\n\t1.904280e+000,\n\t-3.101673e+000,\n\t3.995856e-001,\n\t2.905192e-002,\n\t2.563977e+000,\n\t5.753067e-001,\n\t// albedo 1, turbidity 2\n\t-1.113674e+000,\n\t-1.759694e-001,\n\t-9.754125e+000,\n\t1.087391e+001,\n\t-3.841093e-002,\n\t9.524272e-001,\n\t5.680219e-002,\n\t4.227034e+000,\n\t6.029571e-001,\n\t-1.126496e+000,\n\t-1.680281e-001,\n\t5.332352e+000,\n\t-4.575579e+000,\n\t-6.761755e-002,\n\t3.295335e-001,\n\t1.194896e-001,\n\t5.570901e+000,\n\t4.536185e-001,\n\t-1.103074e+000,\n\t-2.681801e-001,\n\t6.571479e-002,\n\t2.396522e+000,\n\t-4.551280e-001,\n\t2.466331e+000,\n\t-1.232022e-001,\n\t-3.023201e+000,\n\t1.086379e+000,\n\t-1.053299e+000,\n\t-2.697173e-002,\n\t8.379121e-001,\n\t-9.681458e-001,\n\t5.890692e-001,\n\t-4.872027e-001,\n\t2.936929e-001,\n\t7.510139e+000,\n\t3.079122e-001,\n\t-1.079553e+000,\n\t-2.710448e-001,\n\t2.462379e+000,\n\t-3.713554e-001,\n\t-8.534512e+000,\n\t1.828242e+000,\n\t-1.686398e-001,\n\t-1.961340e+000,\n\t8.941077e-001,\n\t-1.069741e+000,\n\t-1.396394e-001,\n\t1.657868e+000,\n\t3.236313e+000,\n\t-2.706344e+000,\n\t-2.948122e-001,\n\t1.314816e-001,\n\t2.868457e+000,\n\t5.413403e-001,\n\t// albedo 1, turbidity 3\n\t-1.131649e+000,\n\t-1.954455e-001,\n\t-7.751595e+000,\n\t8.685861e+000,\n\t-4.910871e-002,\n\t8.992952e-001,\n\t4.710143e-002,\n\t4.254818e+000,\n\t6.821116e-001,\n\t-1.156689e+000,\n\t-1.884324e-001,\n\t3.163519e+000,\n\t-3.091522e+000,\n\t-6.613927e-002,\n\t-2.575883e-002,\n\t1.640065e-001,\n\t6.073643e+000,\n\t4.453468e-001,\n\t-1.079224e+000,\n\t-2.621389e-001,\n\t9.446437e-001,\n\t1.448479e+000,\n\t-3.969384e-001,\n\t2.626638e+000,\n\t-8.101186e-002,\n\t-3.016355e+000,\n\t1.076295e+000,\n\t-1.080832e+000,\n\t1.033057e-002,\n\t-3.500156e-001,\n\t-3.281419e-002,\n\t5.655512e-001,\n\t-1.156742e+000,\n\t4.534710e-001,\n\t8.774122e+000,\n\t2.772869e-001,\n\t-1.051202e+000,\n\t-2.679975e-001,\n\t2.719109e+000,\n\t-2.190316e+000,\n\t-6.878798e+000,\n\t2.250481e+000,\n\t-2.030252e-001,\n\t-2.026527e+000,\n\t9.701096e-001,\n\t-1.089849e+000,\n\t-1.598589e-001,\n\t1.564748e+000,\n\t6.869187e+000,\n\t-3.053670e+000,\n\t-6.110435e-001,\n\t1.644472e-001,\n\t2.370452e+000,\n\t5.511770e-001,\n\t// albedo 1, turbidity 4\n\t-1.171419e+000,\n\t-2.429746e-001,\n\t-8.991334e+000,\n\t9.571216e+000,\n\t-2.772861e-002,\n\t6.688262e-001,\n\t7.683478e-002,\n\t3.785611e+000,\n\t6.347635e-001,\n\t-1.228554e+000,\n\t-2.917562e-001,\n\t2.753986e+000,\n\t-2.491780e+000,\n\t-4.663434e-002,\n\t3.118303e-001,\n\t7.546506e-002,\n\t4.463096e+000,\n\t5.955071e-001,\n\t-1.093124e+000,\n\t-2.447767e-001,\n\t9.097406e-001,\n\t5.448296e-001,\n\t-2.957824e-001,\n\t2.024167e+000,\n\t-5.152333e-004,\n\t-1.069081e+000,\n\t9.369565e-001,\n\t-1.056994e+000,\n\t1.569507e-002,\n\t-8.217491e-001,\n\t1.870818e+000,\n\t7.061930e-001,\n\t-1.483928e+000,\n\t5.978206e-001,\n\t6.864902e+000,\n\t3.673332e-001,\n\t-1.054871e+000,\n\t-2.758129e-001,\n\t2.712807e+000,\n\t-5.950110e+000,\n\t-6.554039e+000,\n\t2.447523e+000,\n\t-1.895171e-001,\n\t-1.454292e+000,\n\t9.131738e-001,\n\t-1.100218e+000,\n\t-1.746241e-001,\n\t1.438505e+000,\n\t1.115481e+001,\n\t-3.266076e+000,\n\t-8.837357e-001,\n\t1.970100e-001,\n\t1.991595e+000,\n\t5.907821e-001,\n\t// albedo 1, turbidity 5\n\t-1.207267e+000,\n\t-2.913610e-001,\n\t-1.103767e+001,\n\t1.140724e+001,\n\t-1.416800e-002,\n\t5.564047e-001,\n\t8.476262e-002,\n\t3.371255e+000,\n\t6.221335e-001,\n\t-1.429698e+000,\n\t-5.374218e-001,\n\t2.837524e+000,\n\t-2.221936e+000,\n\t-2.422337e-002,\n\t9.313758e-002,\n\t7.190250e-002,\n\t1.869022e+000,\n\t5.609035e-001,\n\t-1.002274e+000,\n\t-6.972810e-002,\n\t4.031308e-001,\n\t-3.932997e-001,\n\t-1.521923e-001,\n\t2.390646e+000,\n\t-6.893990e-002,\n\t2.999661e+000,\n\t1.017843e+000,\n\t-1.081168e+000,\n\t-1.178666e-001,\n\t-4.968080e-001,\n\t3.919299e+000,\n\t6.046866e-001,\n\t-2.440615e+000,\n\t7.891538e-001,\n\t2.140835e+000,\n\t2.740470e-001,\n\t-1.050727e+000,\n\t-2.307688e-001,\n\t2.276396e+000,\n\t-9.454407e+000,\n\t-5.505176e+000,\n\t2.992620e+000,\n\t-2.450942e-001,\n\t6.078372e-001,\n\t9.606765e-001,\n\t-1.103752e+000,\n\t-1.810202e-001,\n\t1.375044e+000,\n\t1.589095e+001,\n\t-3.438954e+000,\n\t-1.265669e+000,\n\t2.475172e-001,\n\t1.680768e+000,\n\t5.978056e-001,\n\t// albedo 1, turbidity 6\n\t-1.244324e+000,\n\t-3.378542e-001,\n\t-1.111001e+001,\n\t1.137784e+001,\n\t-7.896794e-003,\n\t4.808023e-001,\n\t9.249904e-002,\n\t3.025816e+000,\n\t5.880239e-001,\n\t-1.593165e+000,\n\t-7.027621e-001,\n\t2.220896e+000,\n\t-1.437709e+000,\n\t-1.534738e-002,\n\t6.286958e-002,\n\t6.644555e-002,\n\t1.091727e+000,\n\t5.470080e-001,\n\t-9.136506e-001,\n\t1.344874e-002,\n\t7.772636e-001,\n\t-1.209396e+000,\n\t-1.408978e-001,\n\t2.433718e+000,\n\t-1.041938e-001,\n\t3.791244e+000,\n\t1.037916e+000,\n\t-1.134968e+000,\n\t-1.803315e-001,\n\t-9.267335e-001,\n\t4.576670e+000,\n\t6.851928e-001,\n\t-2.805000e+000,\n\t8.687208e-001,\n\t1.161483e+000,\n\t2.571688e-001,\n\t-1.017037e+000,\n\t-2.053943e-001,\n\t2.361640e+000,\n\t-9.887818e+000,\n\t-5.122889e+000,\n\t3.287088e+000,\n\t-2.594102e-001,\n\t8.578927e-001,\n\t9.592340e-001,\n\t-1.118723e+000,\n\t-1.934942e-001,\n\t1.226023e+000,\n\t1.674140e+001,\n\t-3.277335e+000,\n\t-1.629809e+000,\n\t2.765232e-001,\n\t1.637713e+000,\n\t6.113963e-001,\n\t// albedo 1, turbidity 7\n\t-1.314779e+000,\n\t-4.119915e-001,\n\t-1.241150e+001,\n\t1.241578e+001,\n\t2.344284e-003,\n\t2.980837e-001,\n\t1.414613e-001,\n\t2.781731e+000,\n\t4.998556e-001,\n\t-1.926199e+000,\n\t-1.020038e+000,\n\t2.569200e+000,\n\t-1.081159e+000,\n\t-2.266833e-002,\n\t3.588668e-001,\n\t8.750078e-003,\n\t-2.452171e-001,\n\t4.796758e-001,\n\t-7.780002e-001,\n\t1.850647e-001,\n\t4.445456e-002,\n\t-2.409297e+000,\n\t-7.816346e-002,\n\t1.546790e+000,\n\t-2.807227e-002,\n\t5.998176e+000,\n\t1.132396e+000,\n\t-1.179326e+000,\n\t-3.578330e-001,\n\t-2.392933e-001,\n\t6.467883e+000,\n\t5.904596e-001,\n\t-1.869975e+000,\n\t8.045839e-001,\n\t-2.498121e+000,\n\t1.610633e-001,\n\t-1.009956e+000,\n\t-1.311896e-001,\n\t1.726577e+000,\n\t-1.219356e+001,\n\t-3.466239e+000,\n\t2.343602e+000,\n\t-2.252205e-001,\n\t2.573681e+000,\n\t1.027109e+000,\n\t-1.112460e+000,\n\t-2.063093e-001,\n\t1.233051e+000,\n\t2.058946e+001,\n\t-4.578074e+000,\n\t-1.145643e+000,\n\t3.160192e-001,\n\t1.420159e+000,\n\t5.860212e-001,\n\t// albedo 1, turbidity 8\n\t-1.371689e+000,\n\t-4.914196e-001,\n\t-1.076610e+001,\n\t1.107405e+001,\n\t-1.485077e-002,\n\t5.936218e-001,\n\t3.685482e-002,\n\t2.599968e+000,\n\t6.002204e-001,\n\t-2.436997e+000,\n\t-1.377939e+000,\n\t2.130141e-002,\n\t1.079593e+000,\n\t-1.796232e-002,\n\t-3.933248e-002,\n\t1.610711e-001,\n\t-6.901181e-001,\n\t1.206416e-001,\n\t-8.743368e-001,\n\t7.331370e-002,\n\t8.734259e-001,\n\t-3.743126e+000,\n\t-3.151167e-002,\n\t1.297596e+000,\n\t-7.634926e-002,\n\t6.532873e+000,\n\t1.435737e+000,\n\t-9.810197e-001,\n\t-3.521634e-001,\n\t-2.855205e-001,\n\t7.134674e+000,\n\t6.839748e-001,\n\t-1.394841e+000,\n\t6.952036e-001,\n\t-4.633104e+000,\n\t-2.173401e-002,\n\t-1.122958e+000,\n\t-1.691536e-001,\n\t1.382360e+000,\n\t-1.102913e+001,\n\t-2.608171e+000,\n\t1.865111e+000,\n\t-1.345154e-001,\n\t3.112342e+000,\n\t1.094134e+000,\n\t-1.075586e+000,\n\t-2.077415e-001,\n\t1.171477e+000,\n\t1.793270e+001,\n\t-4.656858e+000,\n\t-1.036839e+000,\n\t3.338295e-001,\n\t1.042793e+000,\n\t5.739374e-001,\n\t// albedo 1, turbidity 9\n\t-1.465871e+000,\n\t-6.364486e-001,\n\t-8.833718e+000,\n\t9.343650e+000,\n\t-3.223600e-002,\n\t7.552848e-001,\n\t-3.121341e-006,\n\t2.249164e+000,\n\t8.094662e-001,\n\t-2.448924e+000,\n\t-1.270878e+000,\n\t-4.823703e+000,\n\t5.853058e+000,\n\t-2.149127e-002,\n\t3.581132e-002,\n\t-1.230276e-003,\n\t4.892553e-001,\n\t-1.597657e-001,\n\t-2.419809e+000,\n\t-1.071337e+000,\n\t1.575648e+000,\n\t-4.983580e+000,\n\t9.545185e-003,\n\t5.032615e-001,\n\t4.186266e-001,\n\t4.634147e+000,\n\t1.433517e+000,\n\t-1.383278e-001,\n\t-2.797095e-002,\n\t-1.943067e-001,\n\t6.679623e+000,\n\t4.118280e-001,\n\t-2.744289e-001,\n\t-2.118722e-002,\n\t-4.337025e+000,\n\t1.505072e-001,\n\t-1.341872e+000,\n\t-2.518572e-001,\n\t1.027009e+000,\n\t-6.527103e+000,\n\t-1.081271e+000,\n\t1.015465e+000,\n\t2.845789e-001,\n\t2.470371e+000,\n\t9.278120e-001,\n\t-1.040640e+000,\n\t-2.367454e-001,\n\t1.100744e+000,\n\t8.827253e+000,\n\t-4.560794e+000,\n\t-7.287017e-001,\n\t2.842503e-001,\n\t6.336593e-001,\n\t6.327335e-001,\n\t// albedo 1, turbidity 10\n\t-1.877993e+000,\n\t-1.025135e+000,\n\t-4.311037e+000,\n\t4.715016e+000,\n\t-4.711631e-002,\n\t6.335844e-001,\n\t-7.665398e-006,\n\t1.788017e+000,\n\t9.001409e-001,\n\t-2.281540e+000,\n\t-1.137668e+000,\n\t-1.036869e+001,\n\t1.136254e+001,\n\t1.961739e-002,\n\t-9.836174e-002,\n\t-6.734567e-003,\n\t1.320918e+000,\n\t-2.400807e-001,\n\t-4.904054e+000,\n\t-2.315781e+000,\n\t5.735999e+000,\n\t-8.626257e+000,\n\t-1.255643e-001,\n\t1.545446e+000,\n\t1.396860e-001,\n\t2.972897e+000,\n\t1.429934e+000,\n\t4.077067e-001,\n\t-1.833688e-001,\n\t-2.450939e+000,\n\t9.119433e+000,\n\t4.505361e-001,\n\t-1.340828e+000,\n\t3.973690e-001,\n\t-1.785370e+000,\n\t9.628711e-002,\n\t-1.296052e+000,\n\t-3.250526e-001,\n\t1.813294e+000,\n\t-1.031485e+001,\n\t-1.388690e+000,\n\t1.239733e+000,\n\t-8.989196e-002,\n\t-3.389637e-001,\n\t9.639560e-001,\n\t-1.062181e+000,\n\t-2.423444e-001,\n\t7.577592e-001,\n\t1.566938e+001,\n\t-4.462264e+000,\n\t-5.742810e-001,\n\t3.262259e-001,\n\t9.461672e-001,\n\t6.232887e-001,\n};\n\n// [albedo][turbidity][quinticCoeffs]\nfloat kHosekRadX[2][10][6] =\n{\n\t// albedo 0, turbidity 1\n\t1.560219e+000,\n\t1.417388e+000,\n\t1.206927e+000,\n\t1.091949e+001,\n\t5.931416e+000,\n\t7.304788e+000,\n\t// albedo 0, turbidity 2\n\t1.533049e+000,\n\t1.560532e+000,\n\t3.685059e-001,\n\t1.355040e+001,\n\t5.543711e+000,\n\t7.792189e+000,\n\t// albedo 0, turbidity 3\n\t1.471043e+000,\n\t1.746088e+000,\n\t-9.299697e-001,\n\t1.720362e+001,\n\t5.473384e+000,\n\t8.336416e+000,\n\t// albedo 0, turbidity 4\n\t1.355991e+000,\n\t2.109348e+000,\n\t-3.295855e+000,\n\t2.264843e+001,\n\t5.454607e+000,\n\t9.304656e+000,\n\t// albedo 0, turbidity 5\n\t1.244963e+000,\n\t2.547533e+000,\n\t-5.841485e+000,\n\t2.756879e+001,\n\t5.576104e+000,\n\t1.043287e+001,\n\t// albedo 0, turbidity 6\n\t1.175532e+000,\n\t2.784634e+000,\n\t-7.212225e+000,\n\t2.975347e+001,\n\t6.472980e+000,\n\t1.092331e+001,\n\t// albedo 0, turbidity 7\n\t1.082973e+000,\n\t3.118094e+000,\n\t-8.934293e+000,\n\t3.186879e+001,\n\t8.473885e+000,\n\t1.174019e+001,\n\t// albedo 0, turbidity 8\n\t9.692500e-001,\n\t3.349574e+000,\n\t-1.003810e+001,\n\t3.147654e+001,\n\t1.338931e+001,\n\t1.272547e+001,\n\t// albedo 0, turbidity 9\n\t8.547044e-001,\n\t3.151538e+000,\n\t-9.095567e+000,\n\t2.554995e+001,\n\t2.273219e+001,\n\t1.410398e+001,\n\t// albedo 0, turbidity 10\n\t7.580340e-001,\n\t2.311153e+000,\n\t-5.170814e+000,\n\t1.229669e+001,\n\t3.686529e+001,\n\t1.598882e+001,\n\t// albedo 1, turbidity 1\n\t1.664273e+000,\n\t1.574468e+000,\n\t1.422078e+000,\n\t9.768247e+000,\n\t1.447338e+001,\n\t1.644988e+001,\n\t// albedo 1, turbidity 2\n\t1.638295e+000,\n\t1.719586e+000,\n\t5.786675e-001,\n\t1.239846e+001,\n\t1.415419e+001,\n\t1.728605e+001,\n\t// albedo 1, turbidity 3\n\t1.572623e+000,\n\t1.921559e+000,\n\t-7.714802e-001,\n\t1.609246e+001,\n\t1.420954e+001,\n\t1.825908e+001,\n\t// albedo 1, turbidity 4\n\t1.468395e+000,\n\t2.211970e+000,\n\t-2.845869e+000,\n\t2.075027e+001,\n\t1.524822e+001,\n\t1.937622e+001,\n\t// albedo 1, turbidity 5\n\t1.355047e+000,\n\t2.556469e+000,\n\t-4.960920e+000,\n\t2.460237e+001,\n\t1.648360e+001,\n\t2.065648e+001,\n\t// albedo 1, turbidity 6\n\t1.291642e+000,\n\t2.742036e+000,\n\t-6.061967e+000,\n\t2.602002e+001,\n\t1.819144e+001,\n\t2.116712e+001,\n\t// albedo 1, turbidity 7\n\t1.194565e+000,\n\t2.972120e+000,\n\t-7.295779e+000,\n\t2.691805e+001,\n\t2.124880e+001,\n\t2.201819e+001,\n\t// albedo 1, turbidity 8\n\t1.083631e+000,\n\t3.047021e+000,\n\t-7.766096e+000,\n\t2.496261e+001,\n\t2.744264e+001,\n\t2.291875e+001,\n\t// albedo 1, turbidity 9\n\t9.707994e-001,\n\t2.736459e+000,\n\t-6.308284e+000,\n\t1.760860e+001,\n\t3.776291e+001,\n\t2.392150e+001,\n\t// albedo 1, turbidity 10\n\t8.574294e-001,\n\t1.865155e+000,\n\t-2.364707e+000,\n\t4.337793e+000,\n\t5.092831e+001,\n\t2.523432e+001,\n};\n\nfloat kHosekCoeffsY[2][10][6][9] =\n{\n\t// albedo 0, turbidity 1\n\t-1.127942e+000,\n\t-1.905548e-001,\n\t-1.252356e+001,\n\t1.375799e+001,\n\t-3.624732e-002,\n\t1.055453e+000,\n\t1.385036e-002,\n\t4.176970e+000,\n\t5.928345e-001,\n\t-1.155260e+000,\n\t-1.778135e-001,\n\t6.216056e+000,\n\t-5.254116e+000,\n\t-8.787445e-002,\n\t8.434621e-001,\n\t4.025734e-002,\n\t6.195322e+000,\n\t3.111856e-001,\n\t-1.125624e+000,\n\t-3.217593e-001,\n\t5.043919e-001,\n\t1.686284e+000,\n\t-3.536071e-001,\n\t1.476321e+000,\n\t-7.899019e-002,\n\t-4.522531e+000,\n\t1.271691e+000,\n\t-1.081801e+000,\n\t-1.033234e-001,\n\t9.995550e-001,\n\t7.482946e-003,\n\t-6.776018e-002,\n\t1.463141e+000,\n\t9.492021e-002,\n\t5.612723e+000,\n\t1.298846e-001,\n\t-1.075320e+000,\n\t-2.402711e-001,\n\t2.141284e+000,\n\t-1.203359e+000,\n\t-4.945188e+000,\n\t1.437221e+000,\n\t-8.096750e-002,\n\t-1.028378e+000,\n\t1.004164e+000,\n\t-1.073337e+000,\n\t-1.516517e-001,\n\t1.639379e+000,\n\t2.304669e+000,\n\t-3.214244e+000,\n\t1.286245e+000,\n\t5.613957e-002,\n\t2.480902e+000,\n\t4.999363e-001,\n\t// albedo 0, turbidity 2\n\t-1.128399e+000,\n\t-1.857793e-001,\n\t-1.089863e+001,\n\t1.172984e+001,\n\t-3.768099e-002,\n\t9.439285e-001,\n\t4.869335e-002,\n\t4.845114e+000,\n\t6.119211e-001,\n\t-1.114002e+000,\n\t-1.399280e-001,\n\t4.963800e+000,\n\t-4.685500e+000,\n\t-7.780879e-002,\n\t4.049736e-001,\n\t1.586297e-001,\n\t7.770264e+000,\n\t3.449006e-001,\n\t-1.185472e+000,\n\t-3.403543e-001,\n\t6.588322e-001,\n\t1.133713e+000,\n\t-4.118674e-001,\n\t2.061191e+000,\n\t-1.882768e-001,\n\t-4.372586e+000,\n\t1.223530e+000,\n\t-1.002272e+000,\n\t2.000703e-002,\n\t7.073269e-002,\n\t1.485075e+000,\n\t5.005589e-001,\n\t4.301494e-001,\n\t3.626541e-001,\n\t7.921098e+000,\n\t1.574766e-001,\n\t-1.121006e+000,\n\t-3.007777e-001,\n\t2.242051e+000,\n\t-4.571561e+000,\n\t-7.761071e+000,\n\t2.053404e+000,\n\t-1.524018e-001,\n\t-1.886162e+000,\n\t1.018208e+000,\n\t-1.058864e+000,\n\t-1.358673e-001,\n\t1.389667e+000,\n\t8.633409e+000,\n\t-3.437249e+000,\n\t7.295429e-001,\n\t1.514700e-001,\n\t2.842513e+000,\n\t5.014325e-001,\n\t// albedo 0, turbidity 3\n\t-1.144464e+000,\n\t-2.043799e-001,\n\t-1.020188e+001,\n\t1.071247e+001,\n\t-3.256693e-002,\n\t7.860205e-001,\n\t6.872719e-002,\n\t4.824771e+000,\n\t6.259836e-001,\n\t-1.170104e+000,\n\t-2.118626e-001,\n\t4.391405e+000,\n\t-4.198900e+000,\n\t-7.111559e-002,\n\t3.890442e-001,\n\t1.024831e-001,\n\t6.282535e+000,\n\t5.365688e-001,\n\t-1.129171e+000,\n\t-2.552880e-001,\n\t2.238298e-001,\n\t7.314295e-001,\n\t-3.562730e-001,\n\t1.881931e+000,\n\t-3.078716e-002,\n\t-1.039120e+000,\n\t9.096301e-001,\n\t-1.042294e+000,\n\t4.450203e-003,\n\t-5.116033e-001,\n\t2.627589e+000,\n\t6.098996e-001,\n\t-1.264638e-001,\n\t4.325281e-001,\n\t7.080503e+000,\n\t4.583646e-001,\n\t-1.082293e+000,\n\t-2.723056e-001,\n\t2.065076e+000,\n\t-8.143133e+000,\n\t-7.892212e+000,\n\t2.142231e+000,\n\t-7.106240e-002,\n\t-1.122398e+000,\n\t8.338505e-001,\n\t-1.071715e+000,\n\t-1.426568e-001,\n\t1.095351e+000,\n\t1.729783e+001,\n\t-3.851931e+000,\n\t4.360514e-001,\n\t2.114440e-001,\n\t2.970832e+000,\n\t5.944389e-001,\n\t// albedo 0, turbidity 4\n\t-1.195909e+000,\n\t-2.590449e-001,\n\t-1.191037e+001,\n\t1.207947e+001,\n\t-1.589842e-002,\n\t6.297846e-001,\n\t9.054772e-002,\n\t4.285959e+000,\n\t5.933752e-001,\n\t-1.245763e+000,\n\t-3.316637e-001,\n\t4.293660e+000,\n\t-3.694011e+000,\n\t-4.699947e-002,\n\t4.843684e-001,\n\t2.130425e-002,\n\t4.097549e+000,\n\t6.530809e-001,\n\t-1.148742e+000,\n\t-1.902509e-001,\n\t-2.393233e-001,\n\t-2.441254e-001,\n\t-2.610918e-001,\n\t1.846988e+000,\n\t3.532866e-002,\n\t2.660106e+000,\n\t8.358294e-001,\n\t-1.016080e+000,\n\t-7.444960e-002,\n\t-5.053436e-001,\n\t4.388855e+000,\n\t6.054987e-001,\n\t-1.208300e+000,\n\t5.817215e-001,\n\t2.543570e+000,\n\t4.726568e-001,\n\t-1.072027e+000,\n\t-2.101440e-001,\n\t1.518378e+000,\n\t-1.060119e+001,\n\t-6.016546e+000,\n\t2.649475e+000,\n\t-5.166992e-002,\n\t1.571269e+000,\n\t8.344622e-001,\n\t-1.072365e+000,\n\t-1.511201e-001,\n\t7.478010e-001,\n\t1.900732e+001,\n\t-3.950387e+000,\n\t-3.473907e-001,\n\t3.797211e-001,\n\t2.782949e+000,\n\t6.296808e-001,\n\t// albedo 0, turbidity 5\n\t-1.239423e+000,\n\t-3.136289e-001,\n\t-1.351100e+001,\n\t1.349468e+001,\n\t-7.070423e-003,\n\t5.012315e-001,\n\t1.106008e-001,\n\t3.803619e+000,\n\t5.577948e-001,\n\t-1.452524e+000,\n\t-5.676944e-001,\n\t2.993153e+000,\n\t-2.277288e+000,\n\t-2.168954e-002,\n\t3.056720e-001,\n\t1.152338e-002,\n\t1.852697e+000,\n\t6.427228e-001,\n\t-1.061421e+000,\n\t-4.590521e-002,\n\t6.057022e-001,\n\t-1.096835e+000,\n\t-1.504952e-001,\n\t2.344921e+000,\n\t-5.491832e-002,\n\t5.268322e+000,\n\t9.082253e-001,\n\t-1.042373e+000,\n\t-1.769498e-001,\n\t-1.075388e+000,\n\t3.831712e+000,\n\t3.154140e-001,\n\t-2.416458e+000,\n\t7.909032e-001,\n\t-1.492892e-002,\n\t3.854049e-001,\n\t-1.064159e+000,\n\t-1.892684e-001,\n\t1.438685e+000,\n\t-8.166362e+000,\n\t-3.616364e+000,\n\t3.275206e+000,\n\t-1.203825e-001,\n\t2.039491e+000,\n\t8.688057e-001,\n\t-1.070120e+000,\n\t-1.569508e-001,\n\t4.124760e-001,\n\t1.399683e+001,\n\t-3.547085e+000,\n\t-1.046326e+000,\n\t4.973825e-001,\n\t2.791231e+000,\n\t6.503286e-001,\n\t// albedo 0, turbidity 6\n\t-1.283579e+000,\n\t-3.609518e-001,\n\t-1.335397e+001,\n\t1.315248e+001,\n\t-4.431938e-004,\n\t3.769526e-001,\n\t1.429824e-001,\n\t3.573613e+000,\n\t4.998696e-001,\n\t-1.657952e+000,\n\t-7.627948e-001,\n\t1.958222e+000,\n\t-7.949816e-001,\n\t-2.882837e-002,\n\t5.356149e-001,\n\t-5.191946e-002,\n\t8.869955e-001,\n\t6.263320e-001,\n\t-9.527600e-001,\n\t6.494189e-002,\n\t5.361303e-001,\n\t-2.129590e+000,\n\t-9.258630e-002,\n\t1.604776e+000,\n\t5.067770e-002,\n\t6.376055e+000,\n\t9.138052e-001,\n\t-1.080827e+000,\n\t-2.523120e-001,\n\t-7.154262e-001,\n\t4.120085e+000,\n\t1.878228e-001,\n\t-1.492158e+000,\n\t6.881655e-001,\n\t-1.446611e+000,\n\t4.040631e-001,\n\t-1.054075e+000,\n\t-1.665498e-001,\n\t9.191052e-001,\n\t-6.636943e+000,\n\t-1.894826e+000,\n\t2.107810e+000,\n\t-3.680499e-002,\n\t2.655452e+000,\n\t8.413840e-001,\n\t-1.061127e+000,\n\t-1.448849e-001,\n\t2.667493e-001,\n\t1.034103e+001,\n\t-4.285769e+000,\n\t-3.874504e-001,\n\t5.998752e-001,\n\t3.132426e+000,\n\t6.652753e-001,\n\t// albedo 0, turbidity 7\n\t-1.347345e+000,\n\t-4.287832e-001,\n\t-9.305553e+000,\n\t9.133813e+000,\n\t-3.173527e-003,\n\t3.977564e-001,\n\t1.151420e-001,\n\t3.320564e+000,\n\t4.998134e-001,\n\t-1.927296e+000,\n\t-9.901372e-001,\n\t-2.593499e+000,\n\t4.087421e+000,\n\t-5.833993e-002,\n\t8.158929e-001,\n\t-4.681279e-002,\n\t2.423716e-001,\n\t4.938052e-001,\n\t-9.470092e-001,\n\t7.325237e-002,\n\t2.064735e+000,\n\t-5.167540e+000,\n\t-1.313751e-002,\n\t4.832169e-001,\n\t1.126295e-001,\n\t6.970522e+000,\n\t1.035022e+000,\n\t-1.022557e+000,\n\t-2.762616e-001,\n\t-9.375748e-001,\n\t6.696739e+000,\n\t2.200765e-001,\n\t-1.133253e-001,\n\t5.492505e-001,\n\t-3.109391e+000,\n\t3.321914e-001,\n\t-1.087444e+000,\n\t-1.836263e-001,\n\t6.225024e-001,\n\t-8.576765e+000,\n\t-1.107637e+000,\n\t7.859427e-001,\n\t9.910909e-002,\n\t3.112938e+000,\n\t8.596261e-001,\n\t-1.051544e+000,\n\t-1.546262e-001,\n\t2.371731e-001,\n\t1.200502e+001,\n\t-4.527291e+000,\n\t7.268862e-002,\n\t5.571478e-001,\n\t2.532873e+000,\n\t6.662000e-001,\n\t// albedo 0, turbidity 8\n\t-1.375576e+000,\n\t-4.840019e-001,\n\t-8.121290e+000,\n\t8.058140e+000,\n\t-1.445661e-002,\n\t5.123314e-001,\n\t5.813321e-002,\n\t3.203219e+000,\n\t5.442318e-001,\n\t-2.325221e+000,\n\t-1.241463e+000,\n\t-7.063430e+000,\n\t8.741369e+000,\n\t-7.829950e-002,\n\t8.844273e-001,\n\t-3.471106e-002,\n\t1.740583e-001,\n\t2.814079e-001,\n\t-1.228700e+000,\n\t-2.013412e-001,\n\t2.949042e+000,\n\t-7.371945e+000,\n\t1.071753e-001,\n\t-2.491970e-001,\n\t2.265223e-001,\n\t6.391504e+000,\n\t1.172389e+000,\n\t-7.601786e-001,\n\t-1.680631e-001,\n\t-7.584444e-001,\n\t8.541356e+000,\n\t8.222291e-002,\n\t6.729633e-001,\n\t3.206615e-001,\n\t-3.700940e+000,\n\t2.710054e-001,\n\t-1.191166e+000,\n\t-2.672347e-001,\n\t2.927498e-001,\n\t-9.713613e+000,\n\t-4.783721e-001,\n\t2.352803e-001,\n\t2.161949e-001,\n\t2.691481e+000,\n\t8.745447e-001,\n\t-1.030135e+000,\n\t-1.653301e-001,\n\t2.263443e-001,\n\t1.296157e+001,\n\t-4.650644e+000,\n\t7.055709e-003,\n\t5.091975e-001,\n\t2.000370e+000,\n\t6.603839e-001,\n\t// albedo 0, turbidity 9\n\t-1.508018e+000,\n\t-6.460933e-001,\n\t-6.402745e+000,\n\t6.545995e+000,\n\t-3.750320e-002,\n\t6.921803e-001,\n\t3.309819e-003,\n\t2.797527e+000,\n\t6.978446e-001,\n\t-2.333308e+000,\n\t-1.167837e+000,\n\t-1.746787e+001,\n\t1.868630e+001,\n\t-8.948229e-003,\n\t5.621946e-001,\n\t-3.402626e-002,\n\t1.217943e+000,\n\t1.149865e-002,\n\t-2.665953e+000,\n\t-1.226307e+000,\n\t7.169725e+000,\n\t-1.159434e+001,\n\t3.583420e-002,\n\t-3.074378e-001,\n\t3.412248e-001,\n\t4.422122e+000,\n\t1.283791e+000,\n\t-9.705116e-002,\n\t8.312991e-002,\n\t-2.160462e+000,\n\t1.028235e+001,\n\t3.543357e-002,\n\t1.032049e+000,\n\t1.058310e-001,\n\t-2.972898e+000,\n\t2.418628e-001,\n\t-1.329617e+000,\n\t-3.699557e-001,\n\t5.560117e-001,\n\t-9.730113e+000,\n\t9.938865e-002,\n\t-3.071488e-001,\n\t2.510691e-001,\n\t1.777111e+000,\n\t8.705142e-001,\n\t-1.019387e+000,\n\t-1.893247e-001,\n\t1.194079e-001,\n\t1.239436e+001,\n\t-4.799224e+000,\n\t2.940213e-001,\n\t4.841268e-001,\n\t1.529724e+000,\n\t6.582615e-001,\n\t// albedo 0, turbidity 10\n\t-1.896737e+000,\n\t-1.005442e+000,\n\t-6.411032e+000,\n\t6.548220e+000,\n\t-3.227596e-002,\n\t5.717262e-001,\n\t-8.115192e-006,\n\t2.296704e+000,\n\t9.000749e-001,\n\t-2.411116e+000,\n\t-1.225587e+000,\n\t-1.753629e+001,\n\t1.829393e+001,\n\t1.247555e-002,\n\t2.364616e-001,\n\t-5.114637e-003,\n\t1.603778e+000,\n\t-2.224156e-001,\n\t-4.707121e+000,\n\t-2.074977e+000,\n\t7.942300e+000,\n\t-1.132407e+001,\n\t-5.415654e-002,\n\t5.446811e-001,\n\t1.032493e-001,\n\t4.010235e+000,\n\t1.369802e+000,\n\t1.010482e-001,\n\t-4.013305e-001,\n\t-2.674579e+000,\n\t9.779409e+000,\n\t1.782506e-001,\n\t7.053045e-001,\n\t4.200002e-001,\n\t-2.400671e+000,\n\t1.953165e-001,\n\t-1.243526e+000,\n\t-3.391255e-001,\n\t8.848882e-001,\n\t-9.789025e+000,\n\t-3.997324e-001,\n\t-9.546227e-001,\n\t-1.044017e-001,\n\t6.010593e-001,\n\t8.714462e-001,\n\t-1.014633e+000,\n\t-1.730009e-001,\n\t-7.738934e-002,\n\t1.390903e+001,\n\t-4.847307e+000,\n\t1.076059e+000,\n\t5.685743e-001,\n\t1.572992e+000,\n\t6.561432e-001,\n\t// albedo 1, turbidity 1\n\t-1.122998e+000,\n\t-1.881183e-001,\n\t-1.030709e+001,\n\t1.158932e+001,\n\t-4.079495e-002,\n\t9.603774e-001,\n\t3.079436e-002,\n\t4.009235e+000,\n\t5.060745e-001,\n\t-1.134790e+000,\n\t-1.539688e-001,\n\t5.478405e+000,\n\t-4.217270e+000,\n\t-1.043858e-001,\n\t7.165008e-001,\n\t1.524765e-002,\n\t6.473623e+000,\n\t4.207882e-001,\n\t-1.134957e+000,\n\t-3.513318e-001,\n\t7.393837e-001,\n\t1.354415e+000,\n\t-4.764078e-001,\n\t1.690441e+000,\n\t-5.492640e-002,\n\t-5.563523e+000,\n\t1.145743e+000,\n\t-1.058344e+000,\n\t-5.758503e-002,\n\t1.168230e+000,\n\t3.269824e-001,\n\t1.795193e-001,\n\t7.849011e-001,\n\t7.441853e-002,\n\t6.904804e+000,\n\t2.818790e-001,\n\t-1.075194e+000,\n\t-2.355813e-001,\n\t2.463685e+000,\n\t-1.536505e+000,\n\t-7.505771e+000,\n\t9.619712e-001,\n\t-6.465851e-002,\n\t-1.355492e+000,\n\t8.489847e-001,\n\t-1.079030e+000,\n\t-1.465328e-001,\n\t1.773838e+000,\n\t2.310131e+000,\n\t-3.136065e+000,\n\t3.507952e-001,\n\t4.435014e-002,\n\t2.819225e+000,\n\t5.689008e-001,\n\t// albedo 1, turbidity 2\n\t-1.125833e+000,\n\t-1.870849e-001,\n\t-9.555833e+000,\n\t1.059713e+001,\n\t-4.225402e-002,\n\t9.164663e-001,\n\t4.338796e-002,\n\t4.400980e+000,\n\t6.056119e-001,\n\t-1.127440e+000,\n\t-1.551891e-001,\n\t4.755621e+000,\n\t-4.408806e+000,\n\t-7.851763e-002,\n\t2.268284e-001,\n\t1.460070e-001,\n\t7.048003e+000,\n\t3.525997e-001,\n\t-1.143788e+000,\n\t-3.170178e-001,\n\t5.480669e-001,\n\t2.041830e+000,\n\t-4.532139e-001,\n\t2.302233e+000,\n\t-1.887419e-001,\n\t-4.489221e+000,\n\t1.250967e+000,\n\t-1.032849e+000,\n\t7.376031e-003,\n\t5.666073e-001,\n\t-2.312203e-001,\n\t4.862894e-001,\n\t-1.748294e-001,\n\t3.572870e-001,\n\t8.380522e+000,\n\t1.302333e-001,\n\t-1.093728e+000,\n\t-2.786977e-001,\n\t2.641272e+000,\n\t-1.507494e+000,\n\t-8.731243e+000,\n\t1.684055e+000,\n\t-2.023377e-001,\n\t-2.176398e+000,\n\t1.013249e+000,\n\t-1.076578e+000,\n\t-1.456205e-001,\n\t1.693935e+000,\n\t2.945003e+000,\n\t-2.822673e+000,\n\t-2.520033e-001,\n\t1.517034e-001,\n\t2.649109e+000,\n\t5.179094e-001,\n\t// albedo 1, turbidity 3\n\t-1.146417e+000,\n\t-2.119353e-001,\n\t-7.187525e+000,\n\t8.058599e+000,\n\t-5.256438e-002,\n\t8.375733e-001,\n\t3.887093e-002,\n\t4.222111e+000,\n\t6.695347e-001,\n\t-1.173674e+000,\n\t-2.067025e-001,\n\t2.899359e+000,\n\t-2.804918e+000,\n\t-8.473899e-002,\n\t3.944225e-003,\n\t1.340641e-001,\n\t6.160887e+000,\n\t4.527141e-001,\n\t-1.090098e+000,\n\t-2.599633e-001,\n\t9.180856e-001,\n\t1.092710e+000,\n\t-4.215019e-001,\n\t2.427660e+000,\n\t-9.277667e-002,\n\t-2.123523e+000,\n\t1.058159e+000,\n\t-1.084460e+000,\n\t8.056181e-003,\n\t-2.453510e-001,\n\t6.619567e-001,\n\t4.668118e-001,\n\t-9.526719e-001,\n\t4.648454e-001,\n\t8.001572e+000,\n\t3.054194e-001,\n\t-1.053728e+000,\n\t-2.765784e-001,\n\t2.792388e+000,\n\t-3.489517e+000,\n\t-8.150535e+000,\n\t2.195757e+000,\n\t-2.017234e-001,\n\t-2.128017e+000,\n\t9.326589e-001,\n\t-1.099348e+000,\n\t-1.593939e-001,\n\t1.568292e+000,\n\t7.247853e+000,\n\t-2.933000e+000,\n\t-5.890481e-001,\n\t1.724440e-001,\n\t2.433484e+000,\n\t5.736558e-001,\n\t// albedo 1, turbidity 4\n\t-1.185983e+000,\n\t-2.581184e-001,\n\t-7.761056e+000,\n\t8.317053e+000,\n\t-3.351773e-002,\n\t6.676667e-001,\n\t5.941733e-002,\n\t3.820727e+000,\n\t6.324032e-001,\n\t-1.268591e+000,\n\t-3.398067e-001,\n\t2.348503e+000,\n\t-2.023779e+000,\n\t-5.368458e-002,\n\t1.083282e-001,\n\t8.402858e-002,\n\t3.910254e+000,\n\t5.577481e-001,\n\t-1.071353e+000,\n\t-1.992459e-001,\n\t7.878387e-001,\n\t1.974702e-001,\n\t-3.033058e-001,\n\t2.335298e+000,\n\t-8.205259e-002,\n\t7.954454e-001,\n\t9.972312e-001,\n\t-1.089513e+000,\n\t-3.104364e-002,\n\t-5.995746e-001,\n\t2.330281e+000,\n\t6.581939e-001,\n\t-1.821467e+000,\n\t6.679973e-001,\n\t5.090195e+000,\n\t3.125161e-001,\n\t-1.040214e+000,\n\t-2.570934e-001,\n\t2.660489e+000,\n\t-6.506045e+000,\n\t-7.053586e+000,\n\t2.763153e+000,\n\t-2.433632e-001,\n\t-7.648176e-001,\n\t9.452937e-001,\n\t-1.116052e+000,\n\t-1.831993e-001,\n\t1.457694e+000,\n\t1.163608e+001,\n\t-3.216426e+000,\n\t-1.045594e+000,\n\t2.285002e-001,\n\t1.817407e+000,\n\t5.810396e-001,\n\t// albedo 1, turbidity 5\n\t-1.230134e+000,\n\t-3.136264e-001,\n\t-8.909301e+000,\n\t9.145006e+000,\n\t-1.055387e-002,\n\t4.467317e-001,\n\t1.016826e-001,\n\t3.342964e+000,\n\t5.633840e-001,\n\t-1.442907e+000,\n\t-5.593147e-001,\n\t2.156447e+000,\n\t-1.241657e+000,\n\t-3.512130e-002,\n\t3.050274e-001,\n\t1.797175e-002,\n\t1.742358e+000,\n\t5.977153e-001,\n\t-1.027627e+000,\n\t-6.481539e-002,\n\t4.351975e-001,\n\t-1.051677e+000,\n\t-2.030672e-001,\n\t1.942684e+000,\n\t-3.615993e-002,\n\t4.050266e+000,\n\t9.801624e-001,\n\t-1.082110e+000,\n\t-1.578209e-001,\n\t-3.397511e-001,\n\t4.163851e+000,\n\t6.650368e-001,\n\t-1.841730e+000,\n\t7.062544e-001,\n\t6.789881e-001,\n\t3.172623e-001,\n\t-1.047447e+000,\n\t-1.977560e-001,\n\t2.183364e+000,\n\t-8.805249e+000,\n\t-5.483962e+000,\n\t2.551309e+000,\n\t-1.779640e-001,\n\t1.519501e+000,\n\t9.212536e-001,\n\t-1.111853e+000,\n\t-1.935736e-001,\n\t1.394408e+000,\n\t1.392405e+001,\n\t-3.465430e+000,\n\t-1.068432e+000,\n\t2.388671e-001,\n\t1.455336e+000,\n\t6.233425e-001,\n\t// albedo 1, turbidity 6\n\t-1.262238e+000,\n\t-3.546341e-001,\n\t-1.008703e+001,\n\t1.020084e+001,\n\t-1.852187e-003,\n\t3.537580e-001,\n\t1.239199e-001,\n\t3.056093e+000,\n\t5.132052e-001,\n\t-1.613810e+000,\n\t-7.355585e-001,\n\t2.760123e+000,\n\t-1.685253e+000,\n\t-2.517552e-002,\n\t2.914258e-001,\n\t4.743448e-003,\n\t8.689596e-001,\n\t5.674192e-001,\n\t-9.462336e-001,\n\t2.950767e-002,\n\t-2.613816e-001,\n\t-7.398653e-001,\n\t-1.315558e-001,\n\t1.901042e+000,\n\t-6.447844e-002,\n\t4.969341e+000,\n\t1.027342e+000,\n\t-1.111481e+000,\n\t-2.194054e-001,\n\t-9.004538e-002,\n\t3.983442e+000,\n\t4.871278e-001,\n\t-1.965315e+000,\n\t7.956121e-001,\n\t-2.363225e-001,\n\t2.718037e-001,\n\t-1.036397e+000,\n\t-1.827106e-001,\n\t1.964747e+000,\n\t-8.870759e+000,\n\t-4.208011e+000,\n\t2.461215e+000,\n\t-2.158905e-001,\n\t1.561676e+000,\n\t9.436866e-001,\n\t-1.113769e+000,\n\t-1.947819e-001,\n\t1.300720e+000,\n\t1.516476e+001,\n\t-4.088732e+000,\n\t-1.069384e+000,\n\t2.836434e-001,\n\t1.671451e+000,\n\t6.229612e-001,\n\t// albedo 1, turbidity 7\n\t-1.328069e+000,\n\t-4.244047e-001,\n\t-8.417040e+000,\n\t8.552244e+000,\n\t-6.813504e-003,\n\t4.127422e-001,\n\t9.619897e-002,\n\t2.854227e+000,\n\t5.059880e-001,\n\t-1.927552e+000,\n\t-1.025290e+000,\n\t9.529576e-001,\n\t4.255950e-001,\n\t-3.738779e-002,\n\t2.584586e-001,\n\t4.911004e-002,\n\t-2.640913e-001,\n\t4.138626e-001,\n\t-8.488094e-001,\n\t1.435988e-001,\n\t6.356807e-001,\n\t-2.895732e+000,\n\t-8.473961e-002,\n\t1.701305e+000,\n\t-1.323908e-001,\n\t6.499338e+000,\n\t1.210928e+000,\n\t-1.128313e+000,\n\t-3.397048e-001,\n\t-4.043140e-001,\n\t6.265097e+000,\n\t5.482395e-001,\n\t-2.057614e+000,\n\t8.884087e-001,\n\t-2.943879e+000,\n\t9.760301e-002,\n\t-1.039764e+000,\n\t-1.494772e-001,\n\t1.781915e+000,\n\t-1.153012e+001,\n\t-3.379232e+000,\n\t2.517231e+000,\n\t-2.764393e-001,\n\t2.588849e+000,\n\t1.052120e+000,\n\t-1.108447e+000,\n\t-2.012251e-001,\n\t1.198640e+000,\n\t1.925331e+001,\n\t-4.423892e+000,\n\t-1.257122e+000,\n\t3.395690e-001,\n\t1.481220e+000,\n\t5.880175e-001,\n\t// albedo 1, turbidity 8\n\t-1.374185e+000,\n\t-4.967434e-001,\n\t-7.401318e+000,\n\t7.724021e+000,\n\t-2.345723e-002,\n\t5.979653e-001,\n\t2.436346e-002,\n\t2.658970e+000,\n\t6.014891e-001,\n\t-2.310933e+000,\n\t-1.290290e+000,\n\t-1.301909e+000,\n\t2.557806e+000,\n\t-3.744449e-002,\n\t8.982861e-002,\n\t1.090613e-001,\n\t-4.398363e-001,\n\t1.184329e-001,\n\t-1.124730e+000,\n\t-9.921830e-002,\n\t1.366902e+000,\n\t-4.172489e+000,\n\t-5.078016e-002,\n\t1.393597e+000,\n\t-9.323843e-002,\n\t6.452721e+000,\n\t1.435913e+000,\n\t-8.468477e-001,\n\t-2.744819e-001,\n\t-4.347200e-001,\n\t6.713362e+000,\n\t6.127133e-001,\n\t-1.685634e+000,\n\t7.360941e-001,\n\t-4.535502e+000,\n\t-2.920866e-002,\n\t-1.165242e+000,\n\t-2.008697e-001,\n\t1.438778e+000,\n\t-1.008936e+001,\n\t-2.214771e+000,\n\t2.102909e+000,\n\t-1.763085e-001,\n\t2.859075e+000,\n\t1.093470e+000,\n\t-1.074614e+000,\n\t-2.066374e-001,\n\t1.131891e+000,\n\t1.630063e+001,\n\t-4.801441e+000,\n\t-1.112590e+000,\n\t3.595785e-001,\n\t1.122227e+000,\n\t5.794610e-001,\n\t// albedo 1, turbidity 9\n\t-1.521515e+000,\n\t-6.835604e-001,\n\t-5.571044e+000,\n\t6.028774e+000,\n\t-4.253715e-002,\n\t6.875746e-001,\n\t-5.279456e-006,\n\t2.180150e+000,\n\t8.487705e-001,\n\t-2.240415e+000,\n\t-1.171166e+000,\n\t-7.182771e+000,\n\t8.417068e+000,\n\t-1.932866e-002,\n\t1.101887e-001,\n\t-1.098862e-002,\n\t6.242195e-001,\n\t-2.393875e-001,\n\t-2.712354e+000,\n\t-1.198830e+000,\n\t3.180200e+000,\n\t-6.768130e+000,\n\t-2.563386e-003,\n\t7.984607e-001,\n\t2.764376e-001,\n\t4.695358e+000,\n\t1.557045e+000,\n\t-3.655172e-002,\n\t-2.142321e-002,\n\t-9.138120e-001,\n\t7.932786e+000,\n\t3.516542e-001,\n\t-7.994343e-001,\n\t1.786761e-001,\n\t-4.208399e+000,\n\t1.820576e-002,\n\t-1.368610e+000,\n\t-2.656212e-001,\n\t1.249397e+000,\n\t-8.317818e+000,\n\t-8.962772e-001,\n\t1.423249e+000,\n\t1.478381e-001,\n\t2.191660e+000,\n\t1.007748e+000,\n\t-1.041753e+000,\n\t-2.453366e-001,\n\t1.061102e+000,\n\t1.130172e+001,\n\t-4.739312e+000,\n\t-9.223334e-001,\n\t2.982776e-001,\n\t6.162931e-001,\n\t6.080302e-001,\n\t// albedo 1, turbidity 10\n\t-1.989159e+000,\n\t-1.095160e+000,\n\t-2.915550e+000,\n\t3.275339e+000,\n\t-5.735765e-002,\n\t5.742174e-001,\n\t-7.683288e-006,\n\t1.763400e+000,\n\t9.001342e-001,\n\t-2.070020e+000,\n\t-1.086338e+000,\n\t-1.095898e+001,\n\t1.206960e+001,\n\t3.780123e-002,\n\t-1.774699e-002,\n\t-5.881348e-004,\n\t1.333819e+000,\n\t-2.605423e-001,\n\t-5.249653e+000,\n\t-2.383040e+000,\n\t6.160406e+000,\n\t-9.097138e+000,\n\t-1.955319e-001,\n\t1.651785e+000,\n\t6.016463e-004,\n\t3.021824e+000,\n\t1.493574e+000,\n\t4.685432e-001,\n\t-2.358662e-001,\n\t-2.666433e+000,\n\t9.685763e+000,\n\t5.804928e-001,\n\t-1.521875e+000,\n\t5.668989e-001,\n\t-1.548136e+000,\n\t1.688642e-002,\n\t-1.296891e+000,\n\t-3.449031e-001,\n\t1.928548e+000,\n\t-1.167560e+001,\n\t-1.627615e+000,\n\t1.355603e+000,\n\t-1.929074e-001,\n\t-6.568952e-001,\n\t1.009774e+000,\n\t-1.067288e+000,\n\t-2.410392e-001,\n\t7.147961e-001,\n\t1.783840e+001,\n\t-4.374399e+000,\n\t-6.588777e-001,\n\t3.329831e-001,\n\t1.012066e+000,\n\t6.118645e-001,\n};\n\nfloat kHosekRadY[2][10][6] =\n{\n\t// albedo 0, turbidity 1\n\t1.632341e+000,\n\t1.395230e+000,\n\t1.375634e+000,\n\t1.238193e+001,\n\t5.921102e+000,\n\t7.766508e+000,\n\t// albedo 0, turbidity 2\n\t1.597115e+000,\n\t1.554617e+000,\n\t3.932382e-001,\n\t1.505284e+001,\n\t5.725234e+000,\n\t8.158155e+000,\n\t// albedo 0, turbidity 3\n\t1.522034e+000,\n\t1.844545e+000,\n\t-1.322862e+000,\n\t1.918382e+001,\n\t5.440769e+000,\n\t8.837119e+000,\n\t// albedo 0, turbidity 4\n\t1.403048e+000,\n\t2.290852e+000,\n\t-4.013792e+000,\n\t2.485100e+001,\n\t5.521888e+000,\n\t9.845547e+000,\n\t// albedo 0, turbidity 5\n\t1.286364e+000,\n\t2.774498e+000,\n\t-6.648221e+000,\n\t2.964151e+001,\n\t5.923777e+000,\n\t1.097075e+001,\n\t// albedo 0, turbidity 6\n\t1.213544e+000,\n\t3.040195e+000,\n\t-8.092676e+000,\n\t3.186082e+001,\n\t6.789782e+000,\n\t1.158899e+001,\n\t// albedo 0, turbidity 7\n\t1.122622e+000,\n\t3.347465e+000,\n\t-9.649016e+000,\n\t3.343824e+001,\n\t9.347715e+000,\n\t1.231374e+001,\n\t// albedo 0, turbidity 8\n\t1.007356e+000,\n\t3.543858e+000,\n\t-1.053520e+001,\n\t3.239842e+001,\n\t1.483962e+001,\n\t1.331718e+001,\n\t// albedo 0, turbidity 9\n\t8.956642e-001,\n\t3.278700e+000,\n\t-9.254933e+000,\n\t2.557923e+001,\n\t2.489677e+001,\n\t1.476166e+001,\n\t// albedo 0, turbidity 10\n\t7.985143e-001,\n\t2.340404e+000,\n\t-4.928274e+000,\n\t1.141787e+001,\n\t3.961501e+001,\n\t1.682448e+001,\n\t// albedo 1, turbidity 1\n\t1.745162e+000,\n\t1.639467e+000,\n\t1.342721e+000,\n\t1.166033e+001,\n\t1.490124e+001,\n\t1.774031e+001,\n\t// albedo 1, turbidity 2\n\t1.708439e+000,\n\t1.819144e+000,\n\t2.834399e-001,\n\t1.448066e+001,\n\t1.459214e+001,\n\t1.858679e+001,\n\t// albedo 1, turbidity 3\n\t1.631720e+000,\n\t2.094799e+000,\n\t-1.378825e+000,\n\t1.843198e+001,\n\t1.463173e+001,\n\t1.962881e+001,\n\t// albedo 1, turbidity 4\n\t1.516536e+000,\n\t2.438729e+000,\n\t-3.624121e+000,\n\t2.298621e+001,\n\t1.599782e+001,\n\t2.070027e+001,\n\t// albedo 1, turbidity 5\n\t1.405863e+000,\n\t2.785191e+000,\n\t-5.705236e+000,\n\t2.645121e+001,\n\t1.768330e+001,\n\t2.191903e+001,\n\t// albedo 1, turbidity 6\n\t1.344052e+000,\n\t2.951807e+000,\n\t-6.683851e+000,\n\t2.744271e+001,\n\t1.985706e+001,\n\t2.229452e+001,\n\t// albedo 1, turbidity 7\n\t1.245827e+000,\n\t3.182923e+000,\n\t-7.822960e+000,\n\t2.791395e+001,\n\t2.327254e+001,\n\t2.315910e+001,\n\t// albedo 1, turbidity 8\n\t1.132305e+000,\n\t3.202593e+000,\n\t-8.008429e+000,\n\t2.521093e+001,\n\t3.000014e+001,\n\t2.405306e+001,\n\t// albedo 1, turbidity 9\n\t1.020330e+000,\n\t2.820556e+000,\n\t-6.238704e+000,\n\t1.709276e+001,\n\t4.077916e+001,\n\t2.509949e+001,\n\t// albedo 1, turbidity 10\n\t9.031570e-001,\n\t1.863917e+000,\n\t-1.955738e+000,\n\t3.032665e+000,\n\t5.434290e+001,\n\t2.641780e+001,\n};\n\nfloat kHosekCoeffsZ[2][10][6][9] =\n{\n\t// albedo 0, turbidity 1\n\t-1.310023e+000,\n\t-4.407658e-001,\n\t-3.640340e+001,\n\t3.683292e+001,\n\t-8.124762e-003,\n\t5.297961e-001,\n\t1.188633e-002,\n\t3.138320e+000,\n\t5.134778e-001,\n\t-1.424100e+000,\n\t-5.501606e-001,\n\t-1.753510e+001,\n\t1.822769e+001,\n\t-1.539272e-002,\n\t6.366826e-001,\n\t2.661996e-003,\n\t2.659915e+000,\n\t4.071138e-001,\n\t-1.103436e+000,\n\t-1.884105e-001,\n\t6.425322e+000,\n\t-6.910579e+000,\n\t-2.019861e-002,\n\t3.553271e-001,\n\t-1.589061e-002,\n\t5.345985e+000,\n\t8.790218e-001,\n\t-1.186200e+000,\n\t-4.307514e-001,\n\t-3.957947e+000,\n\t5.979352e+000,\n\t-5.348869e-002,\n\t1.736117e+000,\n\t3.491346e-002,\n\t-2.692261e+000,\n\t5.610506e-001,\n\t-1.006038e+000,\n\t-1.305995e-001,\n\t4.473513e+000,\n\t-3.806719e+000,\n\t1.419407e-001,\n\t-2.148238e-002,\n\t-5.081185e-002,\n\t3.735362e+000,\n\t5.358280e-001,\n\t-1.078507e+000,\n\t-1.633754e-001,\n\t-3.812368e+000,\n\t4.381700e+000,\n\t2.988122e-002,\n\t1.754224e+000,\n\t1.472376e-001,\n\t3.722798e+000,\n\t4.999157e-001,\n\t// albedo 0, turbidity 2\n\t-1.333582e+000,\n\t-4.649908e-001,\n\t-3.359528e+001,\n\t3.404375e+001,\n\t-9.384242e-003,\n\t5.587511e-001,\n\t5.726310e-003,\n\t3.073145e+000,\n\t5.425529e-001,\n\t-1.562624e+000,\n\t-7.107068e-001,\n\t-1.478170e+001,\n\t1.559839e+001,\n\t-1.462375e-002,\n\t5.050133e-001,\n\t2.516017e-002,\n\t1.604696e+000,\n\t2.902403e-001,\n\t-8.930158e-001,\n\t4.068077e-002,\n\t1.373481e+000,\n\t-2.342752e+000,\n\t-2.098058e-002,\n\t6.248686e-001,\n\t-5.258363e-002,\n\t7.058214e+000,\n\t1.150373e+000,\n\t-1.262823e+000,\n\t-4.818353e-001,\n\t8.892610e-004,\n\t1.923120e+000,\n\t-4.979718e-002,\n\t1.040693e+000,\n\t1.558103e-001,\n\t-2.852480e+000,\n\t2.420691e-001,\n\t-9.968383e-001,\n\t-1.200648e-001,\n\t1.324342e+000,\n\t-9.430889e-001,\n\t1.931098e-001,\n\t4.436916e-001,\n\t-7.320456e-002,\n\t4.215931e+000,\n\t7.898019e-001,\n\t-1.078185e+000,\n\t-1.718192e-001,\n\t-1.720191e+000,\n\t2.358918e+000,\n\t2.765637e-002,\n\t1.260245e+000,\n\t2.021941e-001,\n\t3.395483e+000,\n\t5.173628e-001,\n\t// albedo 0, turbidity 3\n\t-1.353023e+000,\n\t-4.813523e-001,\n\t-3.104920e+001,\n\t3.140156e+001,\n\t-9.510741e-003,\n\t5.542030e-001,\n\t8.135471e-003,\n\t3.136646e+000,\n\t5.215989e-001,\n\t-1.624704e+000,\n\t-7.990201e-001,\n\t-2.167125e+001,\n\t2.246341e+001,\n\t-1.163533e-002,\n\t5.415746e-001,\n\t2.618378e-002,\n\t1.139214e+000,\n\t3.444357e-001,\n\t-7.983610e-001,\n\t1.417476e-001,\n\t9.914841e+000,\n\t-1.081503e+001,\n\t-1.218845e-002,\n\t3.411392e-001,\n\t-6.137698e-002,\n\t7.445848e+000,\n\t1.180080e+000,\n\t-1.266679e+000,\n\t-4.288977e-001,\n\t-5.818701e+000,\n\t6.986437e+000,\n\t-8.180711e-002,\n\t1.397403e+000,\n\t2.016916e-001,\n\t-1.275731e+000,\n\t2.592773e-001,\n\t-1.009707e+000,\n\t-1.537754e-001,\n\t3.496378e+000,\n\t-3.013726e+000,\n\t2.421150e-001,\n\t-2.831925e-001,\n\t3.003395e-002,\n\t3.702862e+000,\n\t7.746320e-001,\n\t-1.075646e+000,\n\t-1.768747e-001,\n\t-1.347762e+000,\n\t1.989004e+000,\n\t1.375836e-002,\n\t1.764810e+000,\n\t1.330018e-001,\n\t3.230864e+000,\n\t6.626210e-001,\n\t// albedo 0, turbidity 4\n\t-1.375269e+000,\n\t-5.103569e-001,\n\t-3.442661e+001,\n\t3.478703e+001,\n\t-8.460009e-003,\n\t5.408643e-001,\n\t4.813323e-003,\n\t3.016078e+000,\n\t5.062069e-001,\n\t-1.821679e+000,\n\t-9.766461e-001,\n\t-1.926488e+001,\n\t1.997912e+001,\n\t-9.822567e-003,\n\t3.649556e-001,\n\t4.316092e-002,\n\t8.930190e-001,\n\t4.166527e-001,\n\t-6.633542e-001,\n\t1.997841e-001,\n\t2.395592e+000,\n\t-3.117175e+000,\n\t-1.080884e-002,\n\t8.983814e-001,\n\t-1.375825e-001,\n\t6.673463e+000,\n\t1.115663e+000,\n\t-1.303240e+000,\n\t-3.612712e-001,\n\t8.292959e-002,\n\t3.381364e-001,\n\t-6.078648e-002,\n\t3.229247e-001,\n\t3.680987e-001,\n\t7.046755e-001,\n\t3.144924e-001,\n\t-9.952598e-001,\n\t-2.039076e-001,\n\t4.026851e-001,\n\t2.686684e-001,\n\t1.640712e-001,\n\t5.186341e-001,\n\t-1.205520e-002,\n\t2.659613e+000,\n\t8.030394e-001,\n\t-1.098579e+000,\n\t-2.151992e-001,\n\t6.558198e-001,\n\t-7.436900e-004,\n\t-1.421817e-003,\n\t1.073701e+000,\n\t1.886875e-001,\n\t2.536857e+000,\n\t6.673923e-001,\n\t// albedo 0, turbidity 5\n\t-1.457986e+000,\n\t-5.906842e-001,\n\t-3.812464e+001,\n\t3.838539e+001,\n\t-6.024357e-003,\n\t4.741484e-001,\n\t1.209223e-002,\n\t2.818432e+000,\n\t5.012433e-001,\n\t-1.835728e+000,\n\t-1.003405e+000,\n\t-6.848129e+000,\n\t7.601943e+000,\n\t-1.277375e-002,\n\t4.785598e-001,\n\t3.366853e-002,\n\t1.097701e+000,\n\t4.636635e-001,\n\t-8.491348e-001,\n\t9.466365e-003,\n\t-2.685226e+000,\n\t2.004060e+000,\n\t-1.168708e-002,\n\t6.752316e-001,\n\t-1.543371e-001,\n\t5.674759e+000,\n\t1.039534e+000,\n\t-1.083379e+000,\n\t-1.506790e-001,\n\t7.328236e-001,\n\t-5.095568e-001,\n\t-8.609153e-002,\n\t4.448820e-001,\n\t4.174662e-001,\n\t1.481556e+000,\n\t3.942551e-001,\n\t-1.117089e+000,\n\t-3.337605e-001,\n\t2.502281e-001,\n\t4.036323e-001,\n\t2.673899e-001,\n\t2.829817e-001,\n\t2.242450e-002,\n\t2.043207e+000,\n\t7.706902e-001,\n\t-1.071648e+000,\n\t-2.126200e-001,\n\t6.069466e-001,\n\t-1.456290e-003,\n\t-5.515960e-001,\n\t1.046755e+000,\n\t1.985021e-001,\n\t2.290245e+000,\n\t6.876058e-001,\n\t// albedo 0, turbidity 6\n\t-1.483903e+000,\n\t-6.309647e-001,\n\t-4.380213e+001,\n\t4.410537e+001,\n\t-5.712161e-003,\n\t5.195992e-001,\n\t2.028428e-003,\n\t2.687114e+000,\n\t5.098321e-001,\n\t-2.053976e+000,\n\t-1.141473e+000,\n\t5.109183e-001,\n\t8.060391e-002,\n\t-1.033983e-002,\n\t4.066532e-001,\n\t4.869627e-002,\n\t1.161722e+000,\n\t4.039525e-001,\n\t-6.348185e-001,\n\t7.651292e-002,\n\t-1.031327e+001,\n\t1.007598e+001,\n\t-2.083688e-002,\n\t7.359516e-001,\n\t-2.029459e-001,\n\t5.013257e+000,\n\t1.077649e+000,\n\t-1.228630e+000,\n\t-1.650496e-001,\n\t4.077157e-002,\n\t-7.189167e-001,\n\t-5.092220e-002,\n\t2.959814e-001,\n\t5.111496e-001,\n\t2.540433e+000,\n\t3.615330e-001,\n\t-1.041883e+000,\n\t-3.278413e-001,\n\t-6.691911e-002,\n\t1.307364e+000,\n\t2.166663e-001,\n\t3.000595e-001,\n\t-3.157136e-003,\n\t1.389208e+000,\n\t7.999026e-001,\n\t-1.103556e+000,\n\t-2.443602e-001,\n\t4.705347e-001,\n\t-9.296482e-004,\n\t-5.309920e-001,\n\t9.654511e-001,\n\t2.142587e-001,\n\t2.244723e+000,\n\t6.839976e-001,\n\t// albedo 0, turbidity 7\n\t-1.555684e+000,\n\t-6.962113e-001,\n\t-4.647983e+001,\n\t4.674270e+001,\n\t-5.034895e-003,\n\t4.755090e-001,\n\t-9.502561e-007,\n\t2.626569e+000,\n\t5.056194e-001,\n\t-1.998288e+000,\n\t-1.124720e+000,\n\t-1.629586e+000,\n\t2.187993e+000,\n\t-8.284384e-003,\n\t3.845258e-001,\n\t5.726240e-002,\n\t1.185644e+000,\n\t4.255812e-001,\n\t-1.032570e+000,\n\t-2.513850e-001,\n\t-3.721112e+000,\n\t3.506967e+000,\n\t-2.186561e-002,\n\t9.436049e-001,\n\t-2.451412e-001,\n\t4.725724e+000,\n\t1.039256e+000,\n\t-8.597532e-001,\n\t9.073332e-002,\n\t-2.553741e+000,\n\t1.993237e+000,\n\t-4.390891e-002,\n\t-2.046928e-001,\n\t5.515623e-001,\n\t1.909127e+000,\n\t3.948212e-001,\n\t-1.210482e+000,\n\t-4.477622e-001,\n\t-2.267805e-001,\n\t1.219488e+000,\n\t1.336186e-001,\n\t6.866897e-001,\n\t2.808997e-002,\n\t1.600403e+000,\n\t7.816409e-001,\n\t-1.078168e+000,\n\t-2.699261e-001,\n\t2.537282e-001,\n\t3.820684e-001,\n\t-4.425103e-001,\n\t5.298235e-001,\n\t2.185217e-001,\n\t1.728679e+000,\n\t6.882743e-001,\n\t// albedo 0, turbidity 8\n\t-1.697968e+000,\n\t-8.391488e-001,\n\t-5.790105e+001,\n\t5.814120e+001,\n\t-3.404760e-003,\n\t4.265140e-001,\n\t-1.796301e-006,\n\t2.368442e+000,\n\t5.324429e-001,\n\t-2.141552e+000,\n\t-1.172230e+000,\n\t1.677872e+001,\n\t-1.641470e+001,\n\t-5.732425e-003,\n\t2.002199e-001,\n\t6.841834e-002,\n\t1.485338e+000,\n\t3.215763e-001,\n\t-1.442946e+000,\n\t-7.264245e-001,\n\t-9.503706e+000,\n\t9.650462e+000,\n\t-2.120995e-002,\n\t1.419263e+000,\n\t-2.893098e-001,\n\t3.860731e+000,\n\t1.120857e+000,\n\t-5.696752e-001,\n\t3.411279e-001,\n\t-2.931035e-001,\n\t-6.512552e-001,\n\t-1.068437e-001,\n\t-1.085661e+000,\n\t6.107549e-001,\n\t1.459503e+000,\n\t3.210336e-001,\n\t-1.313839e+000,\n\t-5.921371e-001,\n\t-2.332222e-001,\n\t1.648196e+000,\n\t2.492787e-001,\n\t1.381033e+000,\n\t-1.993392e-002,\n\t9.812560e-001,\n\t8.316329e-001,\n\t-1.087464e+000,\n\t-3.195534e-001,\n\t2.902095e-001,\n\t3.383709e-001,\n\t-8.798482e-001,\n\t1.494668e-002,\n\t2.529703e-001,\n\t1.452644e+000,\n\t6.693870e-001,\n\t// albedo 0, turbidity 9\n\t-2.068582e+000,\n\t-1.118605e+000,\n\t-5.081598e+001,\n\t5.097486e+001,\n\t-3.280669e-003,\n\t4.067371e-001,\n\t-2.544951e-006,\n\t2.179497e+000,\n\t5.778017e-001,\n\t-1.744693e+000,\n\t-8.537207e-001,\n\t2.234361e+001,\n\t-2.208318e+001,\n\t-5.932616e-003,\n\t1.035049e-001,\n\t5.742772e-002,\n\t1.977880e+000,\n\t2.124846e-001,\n\t-3.287515e+000,\n\t-2.140268e+000,\n\t-1.249566e+001,\n\t1.240091e+001,\n\t-2.409349e-002,\n\t1.397821e+000,\n\t-2.371627e-001,\n\t2.771192e+000,\n\t1.170496e+000,\n\t5.502311e-001,\n\t1.046630e+000,\n\t2.193517e+000,\n\t-2.220400e+000,\n\t-1.064394e-001,\n\t-1.017926e+000,\n\t4.795457e-001,\n\t1.030644e+000,\n\t3.177516e-001,\n\t-1.719734e+000,\n\t-9.536198e-001,\n\t-6.586821e-001,\n\t1.386361e+000,\n\t-2.513065e-002,\n\t1.187011e+000,\n\t6.542539e-002,\n\t5.296055e-001,\n\t8.082660e-001,\n\t-1.005700e+000,\n\t-3.028096e-001,\n\t4.470957e-002,\n\t1.007760e+000,\n\t-8.119016e-001,\n\t3.153338e-002,\n\t2.311321e-001,\n\t1.182208e+000,\n\t6.824758e-001,\n\t// albedo 0, turbidity 10\n\t-2.728867e+000,\n\t-1.580388e+000,\n\t-3.079627e+001,\n\t3.092586e+001,\n\t-4.197673e-003,\n\t3.154759e-001,\n\t-3.897675e-006,\n\t1.920567e+000,\n\t6.664791e-001,\n\t-1.322495e+000,\n\t-7.249275e-001,\n\t1.477660e+001,\n\t-1.468154e+001,\n\t-9.044857e-003,\n\t5.624314e-002,\n\t6.498392e-002,\n\t2.047389e+000,\n\t6.367540e-002,\n\t-6.102376e+000,\n\t-3.473018e+000,\n\t-9.926071e+000,\n\t9.637797e+000,\n\t-1.097909e-002,\n\t1.103498e+000,\n\t-2.424521e-001,\n\t2.520748e+000,\n\t1.240260e+000,\n\t1.351796e+000,\n\t1.018588e+000,\n\t2.009081e+000,\n\t-1.333394e+000,\n\t-1.979125e-001,\n\t-3.318292e-001,\n\t4.476624e-001,\n\t9.095235e-001,\n\t2.955611e-001,\n\t-1.774467e+000,\n\t-1.079880e+000,\n\t-8.084680e-002,\n\t2.577697e-001,\n\t-1.149295e-001,\n\t4.975303e-001,\n\t2.931611e-003,\n\t-3.803171e-001,\n\t8.002794e-001,\n\t-9.898401e-001,\n\t-2.542513e-001,\n\t-7.530911e-002,\n\t1.870355e+000,\n\t-1.521918e+000,\n\t2.405164e-001,\n\t2.964615e-001,\n\t1.334800e+000,\n\t6.789053e-001,\n\t// albedo 1, turbidity 1\n\t-1.279730e+000,\n\t-4.290674e-001,\n\t-4.277972e+001,\n\t4.343305e+001,\n\t-6.541826e-003,\n\t4.945086e-001,\n\t1.425338e-002,\n\t2.685244e+000,\n\t5.011313e-001,\n\t-1.449506e+000,\n\t-5.766374e-001,\n\t-1.688496e+001,\n\t1.781118e+001,\n\t-1.121649e-002,\n\t3.545020e-001,\n\t2.287338e-002,\n\t1.904281e+000,\n\t4.936998e-001,\n\t-1.021980e+000,\n\t-1.897574e-001,\n\t2.482462e+000,\n\t-2.941725e+000,\n\t-1.570448e-002,\n\t7.532578e-001,\n\t-4.256800e-002,\n\t5.239660e+000,\n\t4.983116e-001,\n\t-1.162608e+000,\n\t-3.428049e-001,\n\t3.974358e+000,\n\t-1.527935e+000,\n\t-3.919201e-002,\n\t8.758593e-001,\n\t7.291363e-002,\n\t-3.455257e+000,\n\t8.007426e-001,\n\t-9.929985e-001,\n\t-8.712006e-002,\n\t-7.397313e-001,\n\t1.348372e+000,\n\t9.511685e-002,\n\t3.233584e-001,\n\t-7.549148e-002,\n\t5.806452e+000,\n\t4.990042e-001,\n\t-1.084996e+000,\n\t-1.739767e-001,\n\t1.580475e-001,\n\t9.088180e-001,\n\t6.871433e-002,\n\t5.933079e-001,\n\t1.188921e-001,\n\t3.074079e+000,\n\t4.999327e-001,\n\t// albedo 1, turbidity 2\n\t-1.317009e+000,\n\t-4.661946e-001,\n\t-4.255347e+001,\n\t4.312782e+001,\n\t-5.727235e-003,\n\t4.285447e-001,\n\t2.189854e-002,\n\t2.608310e+000,\n\t5.190700e-001,\n\t-1.469236e+000,\n\t-6.282139e-001,\n\t-1.241404e+001,\n\t1.348765e+001,\n\t-1.204770e-002,\n\t5.070285e-001,\n\t-7.280216e-004,\n\t1.491533e+000,\n\t3.635064e-001,\n\t-9.713808e-001,\n\t-8.138038e-002,\n\t3.709854e-001,\n\t-1.041174e+000,\n\t-1.814075e-002,\n\t5.060860e-001,\n\t-2.053756e-002,\n\t6.161431e+000,\n\t1.093736e+000,\n\t-1.159057e+000,\n\t-3.698074e-001,\n\t2.711209e+000,\n\t-6.006479e-001,\n\t-4.896926e-002,\n\t9.273957e-001,\n\t1.137712e-001,\n\t-3.496828e+000,\n\t2.867109e-001,\n\t-1.011601e+000,\n\t-8.201890e-002,\n\t2.105725e-001,\n\t4.597520e-001,\n\t1.478925e-001,\n\t2.138940e-001,\n\t-5.660670e-002,\n\t6.057755e+000,\n\t7.859121e-001,\n\t-1.078020e+000,\n\t-1.811580e-001,\n\t1.646622e-001,\n\t8.348426e-001,\n\t1.149064e-001,\n\t4.985738e-001,\n\t1.376605e-001,\n\t2.746607e+000,\n\t4.999626e-001,\n\t// albedo 1, turbidity 3\n\t-1.325672e+000,\n\t-4.769313e-001,\n\t-4.111215e+001,\n\t4.168293e+001,\n\t-6.274997e-003,\n\t4.649469e-001,\n\t1.119411e-002,\n\t2.631267e+000,\n\t5.234546e-001,\n\t-1.619391e+000,\n\t-8.000253e-001,\n\t-1.534098e+001,\n\t1.632706e+001,\n\t-1.012023e-002,\n\t4.242255e-001,\n\t2.931597e-002,\n\t8.925807e-001,\n\t3.314765e-001,\n\t-7.356979e-001,\n\t1.368406e-001,\n\t2.972579e+000,\n\t-3.535359e+000,\n\t-1.318948e-002,\n\t4.607620e-001,\n\t-7.182778e-002,\n\t6.254100e+000,\n\t1.236299e+000,\n\t-1.316217e+000,\n\t-4.194427e-001,\n\t3.489902e-002,\n\t1.289849e+000,\n\t-4.755960e-002,\n\t1.138222e+000,\n\t1.975992e-001,\n\t-8.991542e-001,\n\t2.290572e-001,\n\t-9.502188e-001,\n\t-1.172703e-001,\n\t1.405202e+000,\n\t-3.061919e-001,\n\t1.058772e-001,\n\t-3.760592e-001,\n\t-1.983179e-002,\n\t3.562353e+000,\n\t7.895959e-001,\n\t-1.100117e+000,\n\t-1.900567e-001,\n\t4.925030e-001,\n\t5.250225e-001,\n\t1.576804e-001,\n\t1.042701e+000,\n\t7.330743e-002,\n\t2.796064e+000,\n\t6.749783e-001,\n\t// albedo 1, turbidity 4\n\t-1.354183e+000,\n\t-5.130625e-001,\n\t-4.219268e+001,\n\t4.271772e+001,\n\t-5.365373e-003,\n\t4.136743e-001,\n\t1.235172e-002,\n\t2.520122e+000,\n\t5.187269e-001,\n\t-1.741434e+000,\n\t-9.589761e-001,\n\t-8.230339e+000,\n\t9.296799e+000,\n\t-9.600162e-003,\n\t4.994969e-001,\n\t2.955452e-002,\n\t3.667099e-001,\n\t3.526999e-001,\n\t-6.917347e-001,\n\t2.154887e-001,\n\t-8.760264e-001,\n\t2.334121e-001,\n\t-1.909621e-002,\n\t4.748033e-001,\n\t-1.138514e-001,\n\t6.515360e+000,\n\t1.225097e+000,\n\t-1.293189e+000,\n\t-4.218700e-001,\n\t1.620952e+000,\n\t-7.858597e-001,\n\t-3.769410e-002,\n\t6.636786e-001,\n\t3.364945e-001,\n\t-5.341017e-001,\n\t2.128347e-001,\n\t-9.735521e-001,\n\t-1.325495e-001,\n\t1.007517e+000,\n\t2.598258e-001,\n\t6.762169e-002,\n\t1.421018e-003,\n\t-6.915987e-002,\n\t3.185897e+000,\n\t8.641956e-001,\n\t-1.094800e+000,\n\t-1.962062e-001,\n\t5.755591e-001,\n\t2.906259e-001,\n\t2.625748e-001,\n\t7.644049e-001,\n\t1.347492e-001,\n\t2.677126e+000,\n\t6.465460e-001,\n\t// albedo 1, turbidity 5\n\t-1.393063e+000,\n\t-5.578338e-001,\n\t-4.185249e+001,\n\t4.233504e+001,\n\t-5.435640e-003,\n\t4.743765e-001,\n\t7.422477e-003,\n\t2.442801e+000,\n\t5.211707e-001,\n\t-1.939487e+000,\n\t-1.128509e+000,\n\t-8.974257e+000,\n\t9.978383e+000,\n\t-7.965597e-003,\n\t2.948830e-001,\n\t4.436763e-002,\n\t2.839868e-001,\n\t3.440424e-001,\n\t-6.011562e-001,\n\t2.354877e-001,\n\t-3.079820e+000,\n\t2.585094e+000,\n\t-2.002701e-002,\n\t7.793909e-001,\n\t-1.598414e-001,\n\t5.834678e+000,\n\t1.202856e+000,\n\t-1.315676e+000,\n\t-3.903446e-001,\n\t1.701900e+000,\n\t-1.304609e+000,\n\t-1.045121e-002,\n\t2.747707e-001,\n\t4.143967e-001,\n\t3.197102e-001,\n\t2.637580e-001,\n\t-9.618628e-001,\n\t-1.625841e-001,\n\t1.187138e+000,\n\t1.497802e-001,\n\t-5.590954e-006,\n\t3.178475e-002,\n\t-4.153145e-002,\n\t2.496096e+000,\n\t8.195082e-001,\n\t-1.111554e+000,\n\t-2.365546e-001,\n\t7.831875e-001,\n\t2.018684e-001,\n\t2.074369e-001,\n\t7.395978e-001,\n\t1.225730e-001,\n\t1.876478e+000,\n\t6.821167e-001,\n\t// albedo 1, turbidity 6\n\t-1.427879e+000,\n\t-5.994879e-001,\n\t-3.531016e+001,\n\t3.581581e+001,\n\t-6.431497e-003,\n\t4.554192e-001,\n\t7.348731e-004,\n\t2.334619e+000,\n\t5.233377e-001,\n\t-1.998177e+000,\n\t-1.206633e+000,\n\t-2.146510e+001,\n\t2.242237e+001,\n\t-5.857596e-003,\n\t2.755663e-001,\n\t6.384795e-002,\n\t1.358244e-001,\n\t3.328437e-001,\n\t-6.440630e-001,\n\t2.058571e-001,\n\t2.155499e+000,\n\t-2.587968e+000,\n\t-1.840023e-002,\n\t8.826555e-001,\n\t-2.222452e-001,\n\t5.847073e+000,\n\t1.228387e+000,\n\t-1.229071e+000,\n\t-3.360441e-001,\n\t-3.429599e-001,\n\t6.179469e-001,\n\t2.029610e-003,\n\t8.899319e-002,\n\t5.041624e-001,\n\t1.882964e-001,\n\t2.252040e-001,\n\t-1.022905e+000,\n\t-2.101621e-001,\n\t1.915689e+000,\n\t-6.498794e-001,\n\t-3.463651e-002,\n\t8.954605e-002,\n\t-6.797854e-002,\n\t2.417705e+000,\n\t8.568618e-001,\n\t-1.082538e+000,\n\t-2.007723e-001,\n\t4.731009e-001,\n\t4.077267e-001,\n\t1.324289e-001,\n\t6.514880e-001,\n\t1.702912e-001,\n\t2.309383e+000,\n\t6.600895e-001,\n\t// albedo 1, turbidity 7\n\t-1.472139e+000,\n\t-6.499815e-001,\n\t-3.428465e+001,\n\t3.469659e+001,\n\t-5.747023e-003,\n\t4.174167e-001,\n\t1.688597e-003,\n\t2.323046e+000,\n\t5.395191e-001,\n\t-2.161176e+000,\n\t-1.353089e+000,\n\t-2.226827e+001,\n\t2.329138e+001,\n\t-5.583808e-003,\n\t2.364793e-001,\n\t6.096656e-002,\n\t1.944666e-003,\n\t2.861624e-001,\n\t-6.593044e-001,\n\t1.393558e-001,\n\t4.698373e+000,\n\t-5.193883e+000,\n\t-1.998390e-002,\n\t1.095635e+000,\n\t-2.391254e-001,\n\t5.598103e+000,\n\t1.236193e+000,\n\t-1.195717e+000,\n\t-2.972715e-001,\n\t4.648953e-002,\n\t3.024588e-001,\n\t5.003313e-003,\n\t-3.754741e-001,\n\t5.247265e-001,\n\t-1.381312e-001,\n\t2.493896e-001,\n\t-1.020139e+000,\n\t-2.253524e-001,\n\t3.548437e-001,\n\t7.030485e-001,\n\t-2.107076e-002,\n\t4.581395e-001,\n\t-3.243757e-002,\n\t2.453259e+000,\n\t8.323623e-001,\n\t-1.098770e+000,\n\t-2.435780e-001,\n\t8.761614e-001,\n\t1.941613e-001,\n\t-1.990692e-001,\n\t3.761139e-001,\n\t1.657412e-001,\n\t1.590503e+000,\n\t6.741417e-001,\n\t// albedo 1, turbidity 8\n\t-1.648007e+000,\n\t-8.205121e-001,\n\t-4.435106e+001,\n\t4.479801e+001,\n\t-4.181353e-003,\n\t3.854830e-001,\n\t-1.842385e-006,\n\t2.000281e+000,\n\t5.518363e-001,\n\t-2.140986e+000,\n\t-1.282239e+000,\n\t-3.979213e+000,\n\t4.672459e+000,\n\t-5.008582e-003,\n\t2.421920e-001,\n\t6.253602e-002,\n\t6.612713e-001,\n\t2.555851e-001,\n\t-1.300502e+000,\n\t-5.137898e-001,\n\t5.179821e-001,\n\t-4.032341e-001,\n\t-2.066785e-002,\n\t1.087929e+000,\n\t-2.615309e-001,\n\t4.225887e+000,\n\t1.229237e+000,\n\t-6.963340e-001,\n\t9.241060e-002,\n\t6.936356e-002,\n\t-3.588571e-001,\n\t-5.461843e-002,\n\t-5.616643e-001,\n\t5.484166e-001,\n\t-4.776267e-002,\n\t2.414935e-001,\n\t-1.233179e+000,\n\t-4.325498e-001,\n\t6.479813e-001,\n\t8.368356e-001,\n\t2.458875e-001,\n\t6.464752e-001,\n\t-2.897097e-002,\n\t1.561773e+000,\n\t8.518598e-001,\n\t-1.051023e+000,\n\t-2.533690e-001,\n\t1.004294e+000,\n\t3.028083e-001,\n\t-1.520108e+000,\n\t1.607013e-001,\n\t1.619975e-001,\n\t1.131094e+000,\n\t6.706655e-001,\n\t// albedo 1, turbidity 9\n\t-1.948249e+000,\n\t-1.097383e+000,\n\t-4.453697e+001,\n\t4.494902e+001,\n\t-3.579939e-003,\n\t3.491605e-001,\n\t-2.500253e-006,\n\t1.740442e+000,\n\t6.188022e-001,\n\t-2.154253e+000,\n\t-1.209559e+000,\n\t4.144894e+000,\n\t-3.562411e+000,\n\t-5.638843e-003,\n\t1.067169e-001,\n\t7.594858e-002,\n\t1.005280e+000,\n\t1.072543e-001,\n\t-2.513259e+000,\n\t-1.507208e+000,\n\t-1.602979e+000,\n\t1.404154e+000,\n\t-5.560750e-003,\n\t1.240490e+000,\n\t-2.852117e-001,\n\t3.485252e+000,\n\t1.349321e+000,\n\t-7.832214e-002,\n\t3.655626e-001,\n\t3.856288e-001,\n\t6.867894e-001,\n\t-1.609523e-001,\n\t-6.704306e-001,\n\t5.357301e-001,\n\t-6.457935e-001,\n\t1.479503e-001,\n\t-1.354784e+000,\n\t-5.454375e-001,\n\t8.797469e-001,\n\t-1.466514e+000,\n\t7.134420e-001,\n\t5.934903e-001,\n\t-2.911178e-002,\n\t8.643737e-001,\n\t9.030724e-001,\n\t-1.048324e+000,\n\t-2.738736e-001,\n\t8.783074e-001,\n\t3.246188e+000,\n\t-4.435369e+000,\n\t1.251791e-001,\n\t1.783486e-001,\n\t1.064657e+000,\n\t6.522878e-001,\n\t// albedo 1, turbidity 10\n\t-2.770408e+000,\n\t-1.618911e+000,\n\t-2.504031e+001,\n\t2.531674e+001,\n\t-4.239279e-003,\n\t3.241013e-001,\n\t-3.764484e-006,\n\t1.586843e+000,\n\t7.035906e-001,\n\t-1.913500e+000,\n\t-1.144014e+000,\n\t-1.080587e+001,\n\t1.153677e+001,\n\t-1.003197e-002,\n\t1.577515e-001,\n\t5.217789e-002,\n\t1.225278e+000,\n\t5.172771e-003,\n\t-5.293208e+000,\n\t-2.876463e+000,\n\t2.087053e+000,\n\t-3.201552e+000,\n\t3.892964e-003,\n\t5.323930e-001,\n\t-2.034512e-001,\n\t2.617760e+000,\n\t1.273597e+000,\n\t9.060340e-001,\n\t3.773409e-001,\n\t-6.399945e-001,\n\t3.213979e+000,\n\t-9.112172e-002,\n\t6.494055e-001,\n\t3.953280e-001,\n\t5.047796e-001,\n\t2.998695e-001,\n\t-1.482179e+000,\n\t-6.778310e-001,\n\t1.161775e+000,\n\t-3.004872e+000,\n\t4.774797e-001,\n\t-4.969248e-001,\n\t-3.512074e-003,\n\t-1.307190e+000,\n\t7.927378e-001,\n\t-9.863181e-001,\n\t-1.803364e-001,\n\t5.810824e-001,\n\t4.580570e+000,\n\t-3.863454e+000,\n\t5.328174e-001,\n\t2.272821e-001,\n\t1.771114e+000,\n\t6.791814e-001,\n};\n\nfloat kHosekRadZ[2][10][6] =\n{\n\t// albedo 0, turbidity 1\n\t1.168084e+000,\n\t2.156455e+000,\n\t-3.980314e+000,\n\t1.989302e+001,\n\t1.328335e+001,\n\t1.435621e+001,\n\t// albedo 0, turbidity 2\n\t1.135488e+000,\n\t2.294701e+000,\n\t-4.585886e+000,\n\t2.090208e+001,\n\t1.347840e+001,\n\t1.467658e+001,\n\t// albedo 0, turbidity 3\n\t1.107408e+000,\n\t2.382765e+000,\n\t-5.112357e+000,\n\t2.147823e+001,\n\t1.493128e+001,\n\t1.460882e+001,\n\t// albedo 0, turbidity 4\n\t1.054193e+000,\n\t2.592891e+000,\n\t-6.115000e+000,\n\t2.268967e+001,\n\t1.635672e+001,\n\t1.518999e+001,\n\t// albedo 0, turbidity 5\n\t1.006946e+000,\n\t2.705420e+000,\n\t-6.698930e+000,\n\t2.291830e+001,\n\t1.834324e+001,\n\t1.570651e+001,\n\t// albedo 0, turbidity 6\n\t9.794044e-001,\n\t2.742440e+000,\n\t-6.805283e+000,\n\t2.225271e+001,\n\t2.050797e+001,\n\t1.563130e+001,\n\t// albedo 0, turbidity 7\n\t9.413577e-001,\n\t2.722009e+000,\n\t-6.760707e+000,\n\t2.098242e+001,\n\t2.342588e+001,\n\t1.605011e+001,\n\t// albedo 0, turbidity 8\n\t8.917923e-001,\n\t2.592780e+000,\n\t-6.152635e+000,\n\t1.774141e+001,\n\t2.858324e+001,\n\t1.657910e+001,\n\t// albedo 0, turbidity 9\n\t8.288391e-001,\n\t2.153434e+000,\n\t-4.118327e+000,\n\t1.078118e+001,\n\t3.681710e+001,\n\t1.738139e+001,\n\t// albedo 0, turbidity 10\n\t7.623528e-001,\n\t1.418187e+000,\n\t-8.845235e-001,\n\t7.590129e-001,\n\t4.629859e+001,\n\t1.921657e+001,\n\t// albedo 1, turbidity 1\n\t1.352858e+000,\n\t2.048862e+000,\n\t-2.053393e+000,\n\t1.405874e+001,\n\t3.045344e+001,\n\t3.044430e+001,\n\t// albedo 1, turbidity 2\n\t1.330497e+000,\n\t2.126497e+000,\n\t-2.466296e+000,\n\t1.467559e+001,\n\t3.090738e+001,\n\t3.069707e+001,\n\t// albedo 1, turbidity 3\n\t1.286344e+000,\n\t2.200436e+000,\n\t-2.877228e+000,\n\t1.492701e+001,\n\t3.236288e+001,\n\t3.077223e+001,\n\t// albedo 1, turbidity 4\n\t1.234428e+000,\n\t2.289628e+000,\n\t-3.404699e+000,\n\t1.499436e+001,\n\t3.468390e+001,\n\t3.084842e+001,\n\t// albedo 1, turbidity 5\n\t1.178660e+000,\n\t2.306071e+000,\n\t-3.549159e+000,\n\t1.411006e+001,\n\t3.754188e+001,\n\t3.079730e+001,\n\t// albedo 1, turbidity 6\n\t1.151366e+000,\n\t2.333005e+000,\n\t-3.728627e+000,\n\t1.363374e+001,\n\t3.905894e+001,\n\t3.092599e+001,\n\t// albedo 1, turbidity 7\n\t1.101593e+000,\n\t2.299422e+000,\n\t-3.565787e+000,\n\t1.196745e+001,\n\t4.188472e+001,\n\t3.102755e+001,\n\t// albedo 1, turbidity 8\n\t1.038322e+000,\n\t2.083539e+000,\n\t-2.649585e+000,\n\t8.037389e+000,\n\t4.700869e+001,\n\t3.065948e+001,\n\t// albedo 1, turbidity 9\n\t9.596146e-001,\n\t1.671470e+000,\n\t-8.751538e-001,\n\t1.679772e+000,\n\t5.345784e+001,\n\t3.054520e+001,\n\t// albedo 1, turbidity 10\n\t8.640731e-001,\n\t9.858301e-001,\n\t1.854956e+000,\n\t-6.798097e+000,\n\t5.936468e+001,\n\t3.110255e+001,\n};\n\n#if defined(__clang__)\n#pragma clang diagnostic pop\n#endif\n"
  },
  {
    "path": "LICENSE.md",
    "content": "This is free and unencumbered software released into the public domain.\n\nAnyone is free to copy, modify, publish, use, compile, sell, or\ndistribute this software, either in source code form or as a compiled\nbinary, for any purpose, commercial or non-commercial, and by any\nmeans.\n\nIn jurisdictions that recognize copyright laws, the author or authors\nof this software dedicate any and all copyright interest in the\nsoftware to the public domain. We make this dedication for the benefit\nof the public at large and to the detriment of our heirs and\nsuccessors. We intend this dedication to be an overt act of\nrelinquishment in perpetuity of all present and future rights to this\nsoftware under copyright law.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\nIN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR\nOTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,\nARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR\nOTHER DEALINGS IN THE SOFTWARE.\n\nFor more information, please refer to <http://unlicense.org>\n"
  },
  {
    "path": "Makefile",
    "content": "CXXFLAGS = -std=c++11 -O3\n\nsunsky: SunSky.cpp SunSky.hpp SunSkyTool.cpp\n\t$(CXX) $(CXXFLAGS) -o $@ SunSky.cpp SunSkyTool.cpp\n\nclean:\n\t$(RM) sunsky\n"
  },
  {
    "path": "README.md",
    "content": "Overview\n========\n\nThe code in SunSky.* is an implementation of several sky models:\n\n* The Preetham Clear Sky model. (From [\"A Practical Analytic Model for\n  Daylight\"](https://www.cs.utah.edu/~shirley/papers/sunsky/), Preetham, Shirley\n  & Smits.)\n\n* The Hosek Clear Sky model. (From [\"An Analytic Model for Full Spectral Sky-Dome\n  Radiance\"](https://cgg.mff.cuni.cz/projects/SkylightModelling/), Hosek &\n  Wilkie.)\n\n* Various luminance-only CIE models:\n  * The Clear Sky model\n  * The Overcast Sky model\n  * The Partly(!) Cloudy Sky model\n  * The more recent fifteen CIE sky models from, e.g., \"CIE general sky standard\n    defining luminance distributions\", Darula & Kittler. (There are many variants\n    of this paper!)\n\n* The sun model from Preetham (also used by Hosek), and helpers for converting\n  from time and place to local sun direction.\n\nIn addition, for the Preetham and Hosek models, I have added:\n\n* Table-driven versions for fast evaluation on the GPU (or indeed CPU) via\n  64 x 2 lookup table.\n\n* Extended table-driven versions that use zonal harmonics to produce approximate\n  mirror-to-diffuse BRDF power convolutions. These use 64 x 8 or 64 x 16\n  tables.\n\n* Proper handling of night transitions. The original models assume the sun\n  is above the horizon. The supplied code transitions to a dark blue sky as\n  the sun fully sets, and then to black towards the end of twilight.\n\n* An extension to allow mixing the clear sky models with an overcast sky\n  via a simple linear 'overcast' factor. Generally it's best to use this\n  to simulate high cloud, while low-lying clouds are represented explicitly\n  in the shader. However, when used for shading rather than skybox display,\n  it can also represent low-lying cloud cover.\n\nThe Preetham code is an old standby, and has been shipped in several games.\n\nThe Hosek code is newer, but has also been shipped several times, and is what I\nrecommend now, particularly as it includes an integrated ground albedo factor\nthat feeds into the sky model. It is a re-implementation of the Hosek paper\nusing floats, with some minor optimisations, and an attempt to make the\nstructure a bit more obvious.\n\nSee [sky.sh](sky.sh) for shader routines to evaluate the Hosek sky model,\noptionally with a roughness value, and some notes on how to set up the\ncorresponding uniforms. The file [skybox_fs.sc](skybox_fs.sc) is an example of\nhow to use these in a skybox context -- it also adds a sun disc.\n\n\nResults\n=======\n\nPreetham\n--------\n\nClear Sky:\n\n![](images/preetham-1.png)\n![](images/preetham-2.png)\n![](images/preetham-3.png)\n![](images/preetham-4.png)\n![](images/preetham-5.png)\n![](images/preetham-6.png)\n\nOvercast 50%/Day\n\n![](images/preetham-oc-1.png)\n![](images/preetham-oc-2.png)\n![](images/preetham-oc-3.png)\n![](images/preetham-oc-4.png)\n![](images/preetham-oc-5.png)\n![](images/preetham-oc-6.png)\n<br>\n![](images/preetham-ocd-1.png)\n![](images/preetham-ocd-2.png)\n![](images/preetham-ocd-3.png)\n![](images/preetham-ocd-4.png)\n![](images/preetham-ocd-5.png)\n![](images/preetham-ocd-6.png)\n\nBRDF Day/Sunset:\n\n![](images/preethamBRDF-rd-1.png)\n![](images/preethamBRDF-rd-2.png)\n![](images/preethamBRDF-rd-3.png)\n![](images/preethamBRDF-rd-4.png)\n![](images/preethamBRDF-rd-5.png)\n![](images/preethamBRDF-rd-6.png)\n<br>\n![](images/preethamBRDF-rs-1.png)\n![](images/preethamBRDF-rs-2.png)\n![](images/preethamBRDF-rs-3.png)\n![](images/preethamBRDF-rs-4.png)\n![](images/preethamBRDF-rs-5.png)\n![](images/preethamBRDF-rs-6.png)\n\nHosek\n-----\n\nClear Sky:\n\n![](images/hosek-1.png)\n![](images/hosek-2.png)\n![](images/hosek-3.png)\n![](images/hosek-4.png)\n![](images/hosek-5.png)\n![](images/hosek-6.png)\n\nOvercast 50%/Day:\n\n![](images/hosek-oc-1.png)\n![](images/hosek-oc-2.png)\n![](images/hosek-oc-3.png)\n![](images/hosek-oc-4.png)\n![](images/hosek-oc-5.png)\n![](images/hosek-oc-6.png)\n<br>\n![](images/hosek-ocd-1.png)\n![](images/hosek-ocd-2.png)\n![](images/hosek-ocd-3.png)\n![](images/hosek-ocd-4.png)\n![](images/hosek-ocd-5.png)\n![](images/hosek-ocd-6.png)\n\nBRDF Day/Sunset:\n\n![](images/hosekBRDF-rd-1.png)\n![](images/hosekBRDF-rd-2.png)\n![](images/hosekBRDF-rd-3.png)\n![](images/hosekBRDF-rd-4.png)\n![](images/hosekBRDF-rd-5.png)\n![](images/hosekBRDF-rd-6.png)\n<br>\n![](images/hosekBRDF-rs-1.png)\n![](images/hosekBRDF-rs-2.png)\n![](images/hosekBRDF-rs-3.png)\n![](images/hosekBRDF-rs-4.png)\n![](images/hosekBRDF-rs-5.png)\n![](images/hosekBRDF-rs-6.png)\n\n\nSunSky Tool\n===========\n\nIncluded in SunSkyTool.cpp is a tool exercising most of the sky model\nfunctionality. Current options are below. It can be used to generate top-down\n'hemisphere' views with or without fisheye projection, panoramic views, and\ncube maps, with various forms of tonemapping. Both LDR (png) and HDR (pfm)\nversions are output.\n\nBuilding\n--------\n\nTo build this tool, use 'make', or\n\n    c++ --std=c++11 -O3 SunSky.cpp SunSkyTool.cpp -o sunsky\n\nOr add those files to your favourite IDE.\n\nOptions\n-------\n\n    sunsky <options>\n\n    Options:\n      -h : this help\n      -t <time>          : 0 - 24\n      -d <day of year>   : 0 - 365\n      -b <tubidity>      : 2 - 30\n      -x <ground_bounce> : 0 - 1\n      -l <latitude> <longitude>\n      -w <normalisation weight>\n      -g <gamma>\n      -e <tonemapType> : use given tonemap operator (default: linear)\n      -a : autoscale intensity\n      -i : invert hemisphere\n      -f : fisheye rather than cos projection\n      -c : output cubemap instead\n      -p : output panorama instead\n      -m : output movie, record day as sky.mp4, requires ffmpeg\n      -v : verbose\n      -s <skyType> : use given sky type\n      -r <roughness:float> : specify roughness for PreethamBRDF\n\n    skyType:\n      Preetham         (pt)\n      PreethamTable    (ptt)\n      PreethamBRDF     (ptb)\n      Hosek            (hk)\n      HosekTable       (hkt)\n      HosekBRDF        (hkb)\n      cieClear         (cc)\n      cieOvercast      (co)\n      ciePartlyCloudy  (cp)\n\n    toneMapType:\n      linear           (l)\n      exponential      (ex)\n      reinhard         (rh)\n\nExamples\n--------\n\nShow noon sky for the current time of year using Preetham:\n\n    sunsky -t 12\n\nGlossy version of the same sky:\n\n    sunsky -t 12 -s preethamBRDF -r 0.3\n\nHosek sky at 4pm with greenish albedo, high turbidity, and exponential tone\nmapping, saved to a cube map:\n\n    sunsky -t 16 -s hosek -x 0.2 0.5 0.2 -b 6 -e ex -c\n\n"
  },
  {
    "path": "SunSky.cpp",
    "content": "//\n// SunSky.cpp\n//\n// Implements SunSky.hpp\n//\n// Andrew Willmott, Preetham sun/sky based on code by Brian Smits,\n// Hosek code used L. Hosek & A. Wilkie code Version 1.4a as reference\n//\n\n#include \"SunSky.hpp\"\n\n#include \"HosekDataXYZ.h\"\n#include \"HosekCubic.h\"\n\n#include <stdint.h>\n#include <float.h>\n\n#ifdef _MSC_VER\n    // double->float\n    #pragma warning(disable : 4305)\n#endif\n\nusing namespace SSLib;\n\n// #define SIM_CLAMP                // emulate normalised integer texture, for CPU-side checking\n// #define SUPPORT_OVERCAST_CLAMP   // support overcast functionality via tables. (Without overcast, the theta table is within 0-1 anyway.)\n#define REMAP_THETA                 // remap cos theta to concentrate table around horizon\n#define HOSEK_G_FIX                 // fixes hue ringing during sunset/sunrise with BRDF version of Hosek, causing blue spots opposite\n// #define HOSEK_BRDF_ANALYTIC_H    // for A/B'ing H/FH tables vs. direct ZH evaluation\n// #define LOCAL_DEBUG              // dump debug/tuning info\n\n#ifdef LOCAL_DEBUG\n    #include <stdio.h>\n#endif\n\nnamespace\n{\n    // XYZ/RGB for sRGB primaries + D65 white point\n    const Vec3f kXYZToR( 3.2404542f, -1.5371385f, -0.4985314f);\n    const Vec3f kXYZToG(-0.9692660f,  1.8760108f,  0.0415560f);\n    const Vec3f kXYZToB( 0.0556434f, -0.2040259f,  1.0572252f);\n\n    const Vec3f kRGBToX(0.4124564f,  0.3575761f,  0.1804375f);\n    const Vec3f kRGBToY(0.2126729f,  0.7151522f,  0.0721750f);\n    const Vec3f kRGBToZ(0.0193339f,  0.1191920f,  0.9503041f);\n\n    const Vec2f kClearChroma       (2.0f / 3.0f, 1.0f / 3.0f);\n    const Vec2f kOvercastChroma    (1.0f / 3.0f, 1.0f / 3.0f);\n    const Vec2f kPartlyCloudyChroma(1.0f / 3.0f, 1.0f / 3.0f);\n\n    inline Vec3f xyYToXYZ(const Vec3f& c)\n    {\n        return Vec3f(c.x, c.y, 1.0f - c.x - c.y) * c.z / (c.y + 1e-8f);\n    }\n\n    inline Vec3f xyYToRGB(const Vec3f& xyY)\n    {\n        Vec3f XYZ = xyYToXYZ(xyY);\n\n        return Vec3f\n        (\n            dot(kXYZToR, XYZ),\n            dot(kXYZToG, XYZ),\n            dot(kXYZToB, XYZ)\n        );\n    }\n\n    inline Vec3f XYZToRGB(const Vec3f& XYZ)\n    {\n        return Vec3f\n        (\n            dot(kXYZToR, XYZ),\n            dot(kXYZToG, XYZ),\n            dot(kXYZToB, XYZ)\n        );\n    }\n\n    inline Vec3f RGBToXYZ(const Vec3f& rgb)\n    {\n        return Vec3f\n        (\n            dot(kRGBToX, rgb),\n            dot(kRGBToY, rgb),\n            dot(kRGBToZ, rgb)\n        );\n    }\n\n    inline float RGBToLuminance(const Vec3f& rgb)\n    {\n        return dot(kRGBToY, rgb);\n    }\n\n    inline Vec2f XYZToChroma(Vec3f XYZ)\n    {\n        XYZ += Vec3f(1e-8f);  // avoid divide-by-zero in a way that XYZ=0 -> chroma 1/3, 1/3 (white point)\n        float sum = XYZ.x + XYZ.y + XYZ.z;\n\n        return Vec2f(XYZ.x / sum, XYZ.y / sum);\n    }\n\n    inline uint8_t ToU8(float f)\n    {\n        if (f <= 0.0f)\n            return 0;\n        if (f >= 1.0f)\n            return 255;\n\n        return uint8_t(f * 255.0f + 0.5f);\n    }\n\n    inline float DegreesToRadians(float d)\n    {\n        return d * (vlf_twoPi / 360.0f);\n    }\n\n    inline float ClampUnit(float s)\n    {\n        if (s <= 0.0f)\n            return 0.0f;\n        if (s >= 1.0f)\n            return 1.0f;\n        return s;\n    }\n\n    inline float ClampPositive(float s)\n    {\n        if (s <= 0.0f)\n            return 0.0f;\n        return s;\n    }\n    inline Vec3f ClampPositive3(Vec3f v)\n    {\n        return { ClampPositive(v.x), ClampPositive(v.y), ClampPositive(v.z) };\n    }\n\n#ifdef SIM_CLAMP\n    inline Vec3f ClampUnit(Vec3f v)\n    {\n        return { ClampUnit(v.x), ClampUnit(v.y), ClampUnit(v.z) };\n    }\n#endif\n\n    const float    kFToIBiasF32 = (float)(uint32_t(3) << 22);\n    const int32_t  kFToIBiasS32 = 0x4B400000;\n\n    inline int32_t FloorToSInt32(float s) // force int 'fast' version of this\n    {\n        union\n        {\n            float f;\n            int32_t i;\n        } fi;\n\n        fi.f = floorf(s) + kFToIBiasF32;\n        return fi.i - kFToIBiasS32;\n    }\n\n    inline float LerpClamp(float s)\n    {\n        if (s <= 0.0f)         return 0.0f;\n        if (s >= 1.0f - 1e-6f) return 1.0f - 1e-6f;\n        return s;\n    }\n\n    template<class T_V> T_V LerpSample(float s, int n, const T_V c[])\n    {\n        s = LerpClamp(s);\n        VL_ASSERT(s >= 0.0f && s < 1.0f);\n\n        s *= n - 1;\n\n        int si0 = FloorToSInt32(s);\n        int si1 = (si0 + 1);\n\n        float sf = s - si0;\n\n        return c[si0] * (1 - sf)\n             + c[si1] *    sf   ;\n    }\n\n    template<class T_V> T_V BiLerpSample(float s, float t, int w, int h, const T_V c[])\n    {\n        s = LerpClamp(s);\n        t = LerpClamp(t);\n        VL_ASSERT(s >= 0.0f && t >= 0.0f);\n        VL_ASSERT(s  < 1.0f && t  < 1.0f);\n\n        s *= w - 1;\n        t *= h - 1;\n\n        int si0 = FloorToSInt32(s);\n        int ti0 = FloorToSInt32(t);\n\n        int si1 = (si0 + 1);\n        int ti1 = (ti0 + 1);\n\n        float sf = s - si0;\n        float tf = t - ti0;\n\n        ti0 *= w;\n        ti1 *= w;\n\n        return c[si0 + ti0] * (1 - sf) * (1 - tf)\n             + c[si1 + ti0] *    sf    * (1 - tf)\n             + c[si0 + ti1] * (1 - sf) *    tf\n             + c[si1 + ti1] *    sf    *    tf;\n    }\n}\n\nVec3f SSLib::SunDirection(float timeOfDay, float timeZone, int julianDay, float latitude, float longitude)\n{\n    float solarTime = timeOfDay\n        +  (0.170f * sinf(4 * vlf_pi * (julianDay - 80) / 373)\n          - 0.129f * sinf(2 * vlf_pi * (julianDay -  8) / 355))\n        + (longitude / 15 - timeZone);\n\n    float solarDeclination = (0.4093f * sinf(2 * vlf_pi * (julianDay - 81) / 368));\n\n    float latRads = DegreesToRadians(latitude);\n\n    float sx = cosf(solarDeclination)                 * sinf(vlf_pi * solarTime / 12);\n    float sy = cosf(latRads) * sinf(solarDeclination)\n             + sinf(latRads) * cosf(solarDeclination) * cosf(vlf_pi * solarTime / 12);\n    float sz = sinf(latRads) * sinf(solarDeclination)\n             - cosf(latRads) * cosf(solarDeclination) * cosf(vlf_pi * solarTime / 12);\n\n    return Vec3f(sx, sy, sz);\n}\n\nVec2f SSLib::SunriseAndSunset(float timeZone, int julianDay, float latitude, float longitude)\n{\n    float solarTime =\n        +  (0.170f * sinf(4 * vlf_pi * (julianDay - 80) / 373)\n          - 0.129f * sinf(2 * vlf_pi * (julianDay -  8) / 355))\n        + (longitude / 15 - timeZone);\n\n    float solarDeclination = (0.4093f * sinf(vlf_twoPi * (julianDay - 81) / 368));\n    float latRads = DegreesToRadians(latitude);\n\n    float lhs = tanf(latRads) * tanf(solarDeclination);\n\n    // x = cos(theta) has two solutions for theta: acos(x) and 2pi - acos(x)\n    float h = 24.0f * acosf(lhs) / vlf_twoPi;\n    float sunrise =    h      - solarTime;\n    float sunset  = 24.0f - h - solarTime;\n\n    return Vec2f(sunrise, sunset);\n}\n\nconst float SSLib::kSunDiameter   = 1.392f;\nconst float SSLib::kSunDistance   = 149.6f;\nconst float SSLib::kSunCosAngle   = sqrtf(1.0f - sqr(0.5f * kSunDiameter / kSunDistance));   // = 0.999989\nconst float SSLib::kSunSolidAngle = vlf_twoPi * (1.0f - kSunCosAngle);  // = 6.79998e-05 steradians\n\n\n//------------------------------------------------------------------------------\n// Preetham sun model\n//------------------------------------------------------------------------------\n\nnamespace\n{\n    // From Preetham. Was used by Hosek as sun source, so can be used in conjunction\n    // with either.\n    const float kSunRadiance[16][16][3] =\n    {\n        { {39.4028f, 1.98004f, 5.96046e-08f}, {68821.4f, 29221.3f, 3969.28f}, {189745, 116333, 43283.4f}, {284101, 199843, 103207}, {351488, 265139, 161944}, {400584, 315075, 213163}, {437555, 353806, 256435}, {466261, 384480, 292823}, {489140, 409270, 323569}, {507776, 429675, 349757}, {523235, 446739, 372260}, {536260, 461207, 391767}, {547379, 473621, 408815}, {556978, 484385, 423827}, {565348, 493805, 437137}, {572701, 502106, 449002} },\n        { {34.9717f, 0.0775114f, 0}, {33531, 11971.9f, 875.627f}, {127295, 71095, 22201.3f}, {216301, 142827, 66113.9f}, {285954, 205687, 115900}, {339388, 256990, 163080}, {380973, 298478, 205124}, {414008, 332299, 241816}, {440780, 360220, 273675}, {462869, 383578, 301382}, {481379, 403364, 325586}, {497102, 420314, 346848}, {510615, 434983, 365635}, {522348, 447795, 382333}, {532628, 459074, 397255}, {541698, 469067, 410647} },\n        { {10.0422f, 0, 0.318865f}, {16312.8f, 4886.47f, 84.98f}, {85310.4f, 43421.5f, 11226.2f}, {164586, 102046, 42200.5f}, {232559, 159531, 82822.4f}, {287476, 209581, 124663}, {331656, 251771, 163999}, {367569, 287173, 199628}, {397168, 317025, 231420}, {421906, 342405, 259652}, {442848, 364181, 284724}, {460784, 383030, 307045}, {476303, 399483, 326987}, {489856, 413955, 344876}, {501789, 426774, 360988}, {512360, 438191, 375548} },\n        { {2.3477f, 5.96046e-08f, 0.129991f}, {117.185f, 30.0648f, 0}, {57123.3f, 26502.1f, 5565.4f}, {125170, 72886.2f, 26819.8f}, {189071, 123708, 59081.9f}, {243452, 170892, 95209.2f}, {288680, 212350, 131047}, {326303, 248153, 164740}, {357842, 278989, 195638}, {384544, 305634, 223657}, {407381, 328788, 248954}, {427101, 349038, 271779}, {444282, 366866, 292397}, {459372, 382660, 311064}, {472723, 396734, 328012}, {484602, 409337, 343430} },\n        { {0.383395f, 0, 0.027703f}, {58.0534f, 12.8383f, 0}, {38221.6f, 16163.6f, 2681.55f}, {95147.4f, 52043, 16954.8f}, {153669, 95910.9f, 42062}, {206127, 139327, 72640.8f}, {251236, 179082, 104653}, {289639, 214417, 135896}, {322383, 245500, 165343}, {350467, 272796, 192613}, {374734, 296820, 217644}, {395864, 318050, 240533}, {414400, 336900, 261440}, {430773, 353719, 280544}, {445330, 368800, 298027}, {458337, 382374, 314041} },\n        { {0.0560895f, 0, 0.00474608f}, {44.0061f, 8.32402f, 0}, {25559, 9849.99f, 1237.01f}, {72294.8f, 37148.7f, 10649}, {124859, 74345.6f, 29875.8f}, {174489, 113576, 55359.1f}, {218617, 151011, 83520.3f}, {257067, 185252, 112054}, {290413, 216016, 139698}, {319390, 243473, 165842}, {344686, 267948, 190241}, {366896, 289801, 212852}, {386513, 309371, 233736}, {403942, 326957, 252998}, {419513, 342823, 270764}, {433487, 357178, 287149} },\n        { {0.00811136f, 0, 0.000761211f}, {38.0318f, 6.09287f, 0}, {17083.4f, 5996.83f, 530.476f}, {54909.7f, 26508.7f, 6634.5f}, {101423, 57618.7f, 21163.3f}, {147679, 92573, 42135.2f}, {190207, 127327, 66606.4f}, {228134, 160042, 92352.6f}, {261593, 190061, 117993}, {291049, 217290, 142758}, {317031, 241874, 166258}, {340033, 264051, 188331}, {360490, 284081, 208945}, {378771, 302212, 228135}, {395184, 318667, 245976}, {409974, 333634, 262543} },\n        { {0.00118321f, 0, 0.000119328f}, {34.5228f, 4.62524f, 0}, {11414.1f, 3646.94f, 196.889f}, {41690.9f, 18909.8f, 4091.39f}, {82364.6f, 44646.9f, 14944.8f}, {124966, 75444.4f, 32024.3f}, {165467, 107347, 53075.4f}, {202437, 138252, 76076.7f}, {235615, 167214, 99627}, {265208, 193912, 122858}, {291580, 218327, 145272}, {315124, 240580, 166611}, {336208, 260851, 186761}, {355158, 279331, 205696}, {372256, 296206, 223440}, {387729, 311636, 240030} },\n        { {0.000174701f, 0, 1.84774e-05f}, {31.4054f, 3.4608f, 0}, {7624.24f, 2215.02f, 48.0059f}, {31644.8f, 13484.4f, 2490.1f}, {66872.4f, 34589.1f, 10515}, {105728, 61477.4f, 24300.5f}, {143926, 90494.6f, 42256.1f}, {179617, 119420, 62635.3f}, {212200, 147105, 84088.4f}, {241645, 173041, 105704}, {268159, 197064, 126911}, {292028, 219187, 147374}, {313550, 239512, 166913}, {333008, 258175, 185447}, {350650, 275321, 202953}, {366683, 291081, 219433} },\n        { {2.61664e-05f, 0, 2.86102e-06f}, {27.3995f, 2.42835f, 5.96046e-08f}, {391.889f, 104.066f, 0}, {24013.1f, 9611.97f, 1489.37f}, {54282.4f, 26792.1f, 7366.53f}, {89437, 50090, 18406.3f}, {125174, 76280.7f, 33609.8f}, {159354, 103145, 51538.2f}, {191098, 129407, 70945.4f}, {220163, 154409, 90919.4f}, {246607, 177864, 110847}, {270613, 199690, 130337}, {292410, 219912, 149156}, {312229, 238614, 167173}, {330289, 255902, 184328}, {346771, 271876, 200589} },\n        { {3.93391e-06f, 0, 4.76837e-07f}, {21.8815f, 1.51091f, 0}, {106.645f, 26.2423f, 0}, {18217.8f, 6848.77f, 869.811f}, {44054, 20748.7f, 5134.5f}, {75644.5f, 40807, 13913.2f}, {108852, 64293.6f, 26704.2f}, {141364, 89082.8f, 42380.1f}, {172081, 113831, 59831.4f}, {200579, 137777, 78179.7f}, {226776, 160529, 96794.7f}, {250759, 181920, 115250}, {272686, 201910, 133270}, {292739, 220530, 150685}, {311103, 237847, 167398}, {327934, 253933, 183349} },\n        { {6.55651e-07f, 0, 1.19209e-07f}, {15.4347f, 0.791314f, 0}, {67.98f, 15.4685f, 0}, {13818.5f, 4877.71f, 490.832f}, {35746.5f, 16065.3f, 3556.94f}, {63969.8f, 33240.3f, 10492.5f}, {94648, 54185.5f, 21192.5f}, {125394, 76932.4f, 34825.1f}, {154946, 100125, 50435.6f}, {182726, 122930, 67203.7f}, {208530, 144877, 84504.4f}, {232352, 165726, 101891}, {254283, 185376, 119059}, {274458, 203811, 135807}, {293024, 221062, 152009}, {310113, 237169, 167579} },\n        { {5.96046e-08f, 0, 0}, {9.57723f, 0.336247f, 0}, {52.9113f, 11.1074f, 0}, {10479.8f, 3472.19f, 262.637f}, {29000.9f, 12436.5f, 2445.87f}, {54089.5f, 27073.4f, 7891.84f}, {82288.3f, 45662.7f, 16796.5f}, {111218, 66434.7f, 28595.3f}, {139508, 88064, 42494.5f}, {166453, 109678, 57749.2f}, {191743, 130747, 73756.6f}, {215288, 150968, 90064.3f}, {237114, 170191, 106348}, {257311, 188355, 122384}, {275989, 205455, 138022}, {293255, 221507, 153152} },\n        { {0, 0, 0}, {5.37425f, 0.109694f, 0}, {44.9811f, 8.68891f, 5.96046e-08f}, {7946.76f, 2470.32f, 128.128f}, {23524.7f, 9625.27f, 1666.58f}, {45729.5f, 22047.9f, 5917.85f}, {71535.2f, 38477.1f, 13293.2f}, {98636.4f, 57365.7f, 23460.6f}, {125598, 77452, 35785}, {151620, 97851, 49607}, {176299, 117990, 64359}, {199469, 137520, 79594.4f}, {221098, 156245, 94979.6f}, {241228, 174066, 110274}, {259937, 190947, 125309}, {277307, 206875, 139956} },\n        { {0, 0, 0}, {2.83079f, 0.0199037f, 0}, {40.0718f, 7.10214f, 0}, {6025.35f, 1756.45f, 51.1916f}, {19080.1f, 7447.79f, 1122.67f}, {38657, 17952.9f, 4422.16f}, {62181.1f, 32419.5f, 10503.8f}, {87471.2f, 49531.4f, 19230.6f}, {113069, 68115.1f, 30117.9f}, {138102, 87295.1f, 42596.4f}, {162092, 106474, 56143.2f}, {184805, 125266, 70327.1f}, {206156, 143438, 84812.9f}, {226144, 160857, 99349.8f}, {244814, 177459, 113755}, {262220, 193206, 127887} },\n        { {0, 0, 0}, {1.43779f, 0, 0.00738072f}, {36.6245f, 5.93644f, 0}, {4568.17f, 1248.02f, 9.13028f}, {15473.4f, 5761.51f, 745.266f}, {32674.7f, 14616.6f, 3291.16f}, {54045.1f, 27313.1f, 8284.85f}, {77563.8f, 42764.4f, 15747.9f}, {101783, 59900.8f, 25332.8f}, {125782, 77874.7f, 36561.6f}, {149022, 96078.4f, 48962}, {171213, 114101, 62125.3f}, {192218, 131678, 75721.7f}, {211998, 148648, 89495.8f}, {230564, 164920, 103255}, {247950, 180437, 116847}}\n    };\n}\n\nVec3f SSLib::SunRGB(float cosTheta, float turbidity)\n{\n    if (cosTheta < 0.0f)\n        return vl_0;\n\n    float s = cosTheta;\n    float t = (turbidity - 2.0f) / 10.0f; // useful range is 2-12\n    Vec3f sun = BiLerpSample(s, t, 16, 16, (const Vec3f*) kSunRadiance);\n\n    // 683 converts from watts to candela at 540e12 hz. Really, we should be weighting by luminous efficiency curve rather than CIE_Y\n    sun *=  683.0f;\n    sun *= kSunSolidAngle;\n\n    return sun;\n}\n\nVec2f SSLib::SunChroma(float cosTheta, float turbidity)\n{\n    return XYZToChroma(RGBToXYZ(SunRGB(cosTheta, turbidity)));\n}\n\nfloat SSLib::SunLuminance(float cosTheta, float turbidity)\n{\n    return RGBToLuminance(SunRGB(cosTheta, turbidity));\n}\n\n\n//------------------------------------------------------------------------------\n// CIE models\n//------------------------------------------------------------------------------\n\nfloat SSLib::CIEOvercastSkyLuminance(const Vec3f& v, float Lz)\n{\n    float cosTheta = v.z;\n\n    return Lz * (1.0f + 2.0f * cosTheta) / 3.0f;\n}\n\nfloat SSLib::CIEClearSkyLuminance(const Vec3f& v, const Vec3f& toSun, float Lz)\n{\n    float cosThetaV = v.z;\n\n    if (cosThetaV < 0.0f)\n        cosThetaV = 0.0f;\n\n    float cosThetaS = toSun.z;\n    float thetaS    = acosf(cosThetaS);\n\n    float cosGamma = dot(toSun, v);\n    float gamma    = acosf(cosGamma);\n\n    float top1 = 0.91f + 10 * expf(-3 * gamma ) + 0.45f * sqr(cosGamma);\n    float bot1 = 0.91f + 10 * expf(-3 * thetaS) + 0.45f * sqr(cosThetaS);\n\n    float top2 = 1 - expf(-0.32f / (cosThetaV + 1e-6f));\n    float bot2 = 1 - expf(-0.32f);\n\n    return Lz * (top1 * top2) / (bot1 * bot2);\n}\n\nfloat SSLib::CIEPartlyCloudySkyLuminance(const Vec3f& v, const Vec3f& toSun, float Lz)\n{\n    float cosThetaV = v.z;\n\n    float cosThetaS = toSun.z;\n    float thetaS    = acosf(cosThetaS);\n\n    float cosGamma = dot(toSun, v);\n    float gamma    = acosf(cosGamma);\n\n    float top1 = 0.526f + 5 * expf(-1.5f * gamma);\n    float bot1 = 0.526f + 5 * expf(-1.5f * thetaS);\n\n    float top2 = 1 - expf(-0.8f / (cosThetaV + 1e-6f));\n    float bot2 = 1 - expf(-0.8f);\n\n    return Lz * (top1 * top2) / (bot1 * bot2);\n}\n\n// More modern setup, see Darula & Kittler 2002\nnamespace\n{\n    const float kCIEStandardSkyCoeffs[15][5] =\n    {\n        {  4.0, -0.70,  0, -1.0, 0    },   // Overcast. When normalised this is a fit of the older (1 + 2cos(theta)) / 3 formula found in CIEOvercastSkyLuminance.\n        {  4.0, -0.70,  2, -1.5, 0.15 },   // Overcast, with steep luminance gradation and slight brightening towards the sun\n\n        {  1.1, -0.80,  0, -1.0, 0    },   // Overcast, moderately graded with azimuthal uniformity\n        {  1.1, -0.80,  2, -1.5, 0.15 },   // Overcast, moderately graded and slight brightening towards the sun\n\n        {  0.0, -1.00,  0, -1.0, 0    },   // Sky of uniform luminance\n        {  0.0, -1.00,  2, -1.5, 0.15 },   // Partly cloudy sky, no gradation towards zenith, slight brightening towards the sun\n        {  0.0, -1.00,  5, -2.5, 0.30 },   // Partly cloudy sky, no gradation towards zenith, brighter circumsolar region\n        {  0.0, -1.00, 10, -3.0, 0.45 },   // Partly cloudy sky, no gradation towards zenith, distinct solar corona\n\n        { -1.0, -0.55,  2, -1.5, 0.15 },   // Partly cloudy, with the obscured sun\n        { -1.0, -0.55,  5, -2.5, 0.30 },   // Partly cloudy, with brighter circumsolar region\n        { -1.0, -0.55, 10, -3.0, 0.45 },   // White-blue sky with distinct solar corona\n\n        { -1.0, -0.32, 10, -3.0, 0.45 },   // CIE Standard Clear Sky, low illuminance turbidity. T <= 2.45\n        { -1.0, -0.32, 16, -3.0, 0.30 },   // CIE Standard Clear Sky, polluted atmosphere\n\n        { -1.0, -0.15, 16, -3.0, 0.30 },   // Cloudless turbid sky with broad solar corona\n        { -1.0, -0.15, 24, -2.8, 0.15 },   // White-blue turbid sky with broad solar corona\n    };\n\n    inline float CIELumRatio(const Vec3f& v, const Vec3f& toSun, float a, float b, float c, float d, float e)\n    {\n        float cosThetaV = v.z;\n\n        float cosThetaS = toSun.z;\n        float thetaS    = acosf(cosThetaS);\n\n        float cosGamma = dot(toSun, v);\n        float gamma    = acosf(cosGamma);\n\n        float top1 = 1.0f + a * expf(b / (cosThetaV + 1e-6f));\n        float bot1 = 1.0f + a * expf(b);\n\n        float top2 = 1.0f + c * expf(d * gamma ) + e * sqr(cosGamma);\n        float bot2 = 1.0f + c * expf(d * thetaS) + e * sqr(cosThetaS);\n\n        return (top1 * top2) / (bot1 * bot2);\n    }\n}\n\nfloat SSLib::CIEStandardSky(int type, const Vec3f& v, const Vec3f& toSun, float Lz)\n{\n    VL_ASSERT(type >= 0 && type < 15);\n    const float* c = kCIEStandardSkyCoeffs[type];\n    return CIELumRatio(v, toSun, c[0], c[1], c[2], c[3], c[4]) * Lz;\n}\n\nfloat SSLib::ZenithLuminance(float thetaS, float T)\n{\n    float chi = (4.0f / 9.0f - T / 120.0f) * (vlf_pi - 2.0f * thetaS);\n    float Lz = (4.0453f * T - 4.9710f) * tanf(chi) - 0.2155f * T + 2.4192f;\n    Lz *= 1000.0;   // conversion from kcd/m^2 to cd/m^2\n    return Lz;\n}\n\n\n\n\n\n//------------------------------------------------------------------------------\n// - Preetham\n//------------------------------------------------------------------------------\n\nnamespace\n{\n    //\n    // The Perez function is as follows:\n    //\n    //    P(t, g) =  (1 + A e ^ (B / cos(t))) (1 + C e ^ (D g)  + E cos(g)  ^ 2)\n    //               --------------------------------------------------------\n    //               (1 + A e ^ B)            (1 + C e ^ (D ts) + E cos(ts) ^ 2)\n    //\n\n    // A: sky\n    // B: sky tightness\n    // C: sun\n    // D: sun tightness, higher = tighter\n    // E: rosy hue around sun\n\n    inline float PerezUpper(const float* lambdas, float cosTheta, float gamma, float cosGamma)\n    {\n        return  (1.0f + lambdas[0] * expf(lambdas[1] / (cosTheta + 1e-6f)))\n              * (1.0f + lambdas[2] * expf(lambdas[3] * gamma) + lambdas[4] * sqr(cosGamma));\n    }\n\n    inline float PerezLower(const float* lambdas, float cosThetaS, float thetaS)\n    {\n        return  (1.0f + lambdas[0] * expf(lambdas[1]))\n              * (1.0f + lambdas[2] * expf(lambdas[3] * thetaS) + lambdas[4] * sqr(cosThetaS));\n    }\n}\n\nSkyPreetham::SkyPreetham() :\n    mToSun(vl_z),\n\n    // mPerez_x[5] = {}\n    // mPerez_y[5] = {}\n    // mPerez_Y[5] = {}\n    mZenith(vl_0),\n    mPerezInvDen(vl_1)\n{\n}\n\nvoid SkyPreetham::Update(const Vec3f& sun, float turbidity, float overcast, float horizCrush)\n{\n    mToSun = sun;\n\n    float   T = turbidity;\n\n    mPerez_Y[0] =  0.17872f * T - 1.46303f;\n    mPerez_Y[1] = -0.35540f * T + 0.42749f;\n    mPerez_Y[2] = -0.02266f * T + 5.32505f;\n    mPerez_Y[3] =  0.12064f * T - 2.57705f;\n    mPerez_Y[4] = -0.06696f * T + 0.37027f;\n\n    mPerez_x[0] = -0.01925f * T - 0.25922f;\n    mPerez_x[1] = -0.06651f * T + 0.00081f;\n    mPerez_x[2] = -0.00041f * T + 0.21247f;\n    mPerez_x[3] = -0.06409f * T - 0.89887f;\n    mPerez_x[4] = -0.00325f * T + 0.04517f;\n\n    mPerez_y[0] = -0.01669f * T - 0.26078f;\n    mPerez_y[1] = -0.09495f * T + 0.00921f;\n    mPerez_y[2] = -0.00792f * T + 0.21023f;\n    mPerez_y[3] = -0.04405f * T - 1.65369f;\n    mPerez_y[4] = -0.01092f * T + 0.05291f;\n\n    float cosTheta = mToSun.z;\n    float theta  = acosf(cosTheta);    // angle from zenith rather than horizon\n    float theta2 = sqr(theta);\n    float theta3 = theta2 * theta;\n    float T2 = sqr(T);\n\n    // mZenith stored as xyY\n    mZenith.z = ZenithLuminance(theta, T);\n\n    mZenith.x =\n        ( 0.00165f * theta3 - 0.00374f * theta2 + 0.00208f * theta + 0.00000f) * T2 +\n        (-0.02902f * theta3 + 0.06377f * theta2 - 0.03202f * theta + 0.00394f) * T  +\n        ( 0.11693f * theta3 - 0.21196f * theta2 + 0.06052f * theta + 0.25885f);\n\n    mZenith.y =\n        ( 0.00275f * theta3 - 0.00610f * theta2 + 0.00316f * theta + 0.00000f) * T2 +\n        (-0.04214f * theta3 + 0.08970f * theta2 - 0.04153f * theta + 0.00515f) * T  +\n        ( 0.15346f * theta3 - 0.26756f * theta2 + 0.06669f * theta + 0.26688f);\n\n    // Adjustments (extensions)\n\n    if (cosTheta < 0.0f)    // Handle sun going below the horizon\n    {\n        float s = ClampUnit(1.0f + cosTheta * 50.0f);   // goes from 1 to 0 as the sun sets\n\n        // Take C/E which control sun term to zero\n        mPerez_x[2] *= s;\n        mPerez_y[2] *= s;\n        mPerez_Y[2] *= s;\n        mPerez_x[4] *= s;\n        mPerez_y[4] *= s;\n        mPerez_Y[4] *= s;\n    }\n\n    if (overcast != 0.0f)      // Handle overcast term\n    {\n        float invOvercast = 1.0f - overcast;\n\n        // lerp back towards unity\n        mPerez_x[0] *= invOvercast;  // main sky chroma -> base\n        mPerez_y[0] *= invOvercast;\n\n        // sun flare -> 0 strength/base chroma\n        mPerez_x[2] *= invOvercast;\n        mPerez_y[2] *= invOvercast;\n        mPerez_Y[2] *= invOvercast;\n        mPerez_x[4] *= invOvercast;\n        mPerez_y[4] *= invOvercast;\n        mPerez_Y[4] *= invOvercast;\n\n        // lerp towards a fit of the CIE cloudy sky model: 4, -0.7\n        mPerez_Y[0] = lerp(mPerez_Y[0],  4.0f, overcast);\n        mPerez_Y[1] = lerp(mPerez_Y[1], -0.7f, overcast);\n\n        // lerp base colour towards white point\n        mZenith.x = mZenith.x * invOvercast + 0.333f * overcast;\n        mZenith.y = mZenith.y * invOvercast + 0.333f * overcast;\n    }\n\n    if (horizCrush != 0.0f)\n    {\n        // The Preetham sky model has a \"muddy\" horizon, which can be objectionable in\n        // typical game views. We allow artistic control over it.\n        mPerez_Y[1] *= horizCrush;\n        mPerez_x[1] *= horizCrush;\n        mPerez_y[1] *= horizCrush;\n    }\n\n    // initialize sun-constant parts of the Perez functions\n\n    Vec3f perezLower    // denominator terms are dependent on sun angle only\n    (\n        PerezLower(mPerez_x, cosTheta, theta),\n        PerezLower(mPerez_y, cosTheta, theta),\n        PerezLower(mPerez_Y, cosTheta, theta)\n    );\n\n    mPerezInvDen = mZenith / perezLower;\n}\n\nVec3f SkyPreetham::SkyRGB(const Vec3f& v) const\n{\n    float cosTheta = v.z;\n    float cosGamma = dot(mToSun, v);\n    float gamma    = acosf(cosGamma);\n\n    if (cosTheta < 0.0f)\n        cosTheta = 0.0f;\n\n    Vec3f xyY\n    (\n        PerezUpper(mPerez_x, cosTheta, gamma, cosGamma),\n        PerezUpper(mPerez_y, cosTheta, gamma, cosGamma),\n        PerezUpper(mPerez_Y, cosTheta, gamma, cosGamma)\n    );\n\n    xyY *= mPerezInvDen;\n\n    return xyYToRGB(xyY);\n}\n\nfloat SkyPreetham::SkyLuminance(const Vec3f& v) const\n{\n    float cosTheta = v.z;\n    float cosGamma = dot(mToSun, v);\n    float gamma    = acosf(cosGamma);\n\n    if (cosTheta < 0.0f)\n        cosTheta = 0.0f;\n\n    return PerezUpper(mPerez_Y, cosTheta, gamma, cosGamma) * mPerezInvDen.z;\n}\n\nVec2f SkyPreetham::SkyChroma(const Vec3f& v) const\n{\n    float cosTheta = v.z;\n    float cosGamma = dot(mToSun, v);\n    float gamma    = acosf(cosGamma);\n\n    if (cosTheta < 0.0f)\n        cosTheta = 0.0f;\n\n    return Vec2f\n    (\n        PerezUpper(mPerez_x, cosTheta, gamma, cosGamma) * mPerezInvDen.x,\n        PerezUpper(mPerez_y, cosTheta, gamma, cosGamma) * mPerezInvDen.y\n    );\n}\n\n\n//------------------------------------------------------------------------------\n// SkyHosek\n//------------------------------------------------------------------------------\n\nnamespace\n{\n    inline float EvalQuintic(const float w[6], const float data[6])\n    {\n        return    w[0] * data[0]\n                + w[1] * data[1]\n                + w[2] * data[2]\n                + w[3] * data[3]\n                + w[4] * data[4]\n                + w[5] * data[5];\n    }\n\n    inline void EvalQuintic(const float w[6], const float data[6][9], float coeffs[9])\n    {\n        for (int i = 0; i < 9; i++)\n        {\n            coeffs[i] =   w[0] * data[0][i]\n                        + w[1] * data[1][i]\n                        + w[2] * data[2][i]\n                        + w[3] * data[3][i]\n                        + w[4] * data[4][i]\n                        + w[5] * data[5][i];\n        }\n    }\n\n    inline void FindQuinticWeights(float s, float w[6])\n    {\n        float s1 = s;\n        float s2 = s1 * s1;\n        float s3 = s1 * s2;\n        float s4 = s2 * s2;\n        float s5 = s2 * s3;\n\n        float is1 = 1.0f - s1;\n        float is2 = is1 * is1;\n        float is3 = is1 * is2;\n        float is4 = is2 * is2;\n        float is5 = is2 * is3;\n\n        w[0] = is5;\n        w[1] = is4 * s1 *  5.0f;\n        w[2] = is3 * s2 * 10.0f;\n        w[3] = is2 * s3 * 10.0f;\n        w[4] = is1 * s4 *  5.0f;\n        w[5] =       s5;\n    }\n\n\n    float FindHosekCoeffs\n    (\n        const float   dataset9[2][10][6][9],    // albedo x 2, turbidity x 10, quintics x 6, weights x 9\n        const float   datasetR[2][10][6],       // albedo x 2, turbidity x 10, quintics x 6\n        float         turbidity,\n        float         albedo,\n        float         solarElevation,\n        float         coeffs[9]\n    )\n    {\n        int tbi = int(floorf(turbidity));\n\n        if (tbi < 1)\n            tbi = 1;\n        else if (tbi > 9)\n            tbi = 9;\n\n        float tbf = turbidity - tbi;\n\n        const float s = powf(solarElevation / vlf_halfPi, (1.0f / 3.0f));\n\n        float quinticWeights[6];\n        FindQuinticWeights(s, quinticWeights);\n\n        float ic[4][9];\n\n        EvalQuintic(quinticWeights, dataset9[0][tbi - 1], ic[0]);\n        EvalQuintic(quinticWeights, dataset9[1][tbi - 1], ic[1]);\n        EvalQuintic(quinticWeights, dataset9[0][tbi    ], ic[2]);\n        EvalQuintic(quinticWeights, dataset9[1][tbi    ], ic[3]);\n\n        float ir[4] =\n        {\n            EvalQuintic(quinticWeights, datasetR[0][tbi - 1]),\n            EvalQuintic(quinticWeights, datasetR[1][tbi - 1]),\n            EvalQuintic(quinticWeights, datasetR[0][tbi    ]),\n            EvalQuintic(quinticWeights, datasetR[1][tbi    ]),\n        };\n\n        float cw[4] =\n        {\n            (1.0f - albedo) * (1.0f - tbf),\n            albedo          * (1.0f - tbf),\n            (1.0f - albedo) * tbf,\n            albedo          * tbf,\n        };\n\n        for (int i = 0; i < 9; i++)\n            coeffs[i] = cw[0] * ic[0][i]\n                      + cw[1] * ic[1][i]\n                      + cw[2] * ic[2][i]\n                      + cw[3] * ic[3][i];\n\n        return cw[0] * ir[0] + cw[1] * ir[1] + cw[2] * ir[2] + cw[3] * ir[3];\n    }\n\n    inline Vec4f CubicWeights(float s)\n    {\n        float s1 = s;\n        float s2 = s1 * s1;\n        float s3 = s1 * s2;\n\n        float is1 = 1.0f - s1;\n        float is2 = is1 * is1;\n        float is3 = is1 * is2;\n\n        return Vec4f(is3, is2 * s1 * 3.0f, is1 * s2 * 3.0f, s3);\n    }\n\n    float FindHosekCoeffs\n    (\n        const float   datasetC[10][4][2][4],\n        float         turbidity,\n        float         albedo,\n        float         solarElevation,\n        float         coeffs[9]\n    )\n    {\n        const float t = (turbidity - 1.0f) / 9.0f;\n        const float s = powf(solarElevation / vlf_halfPi, (1.0f / 3.0f));\n\n        Vec4f wt = CubicWeights(t);\n        Vec4f ws = CubicWeights(s);\n\n        float rad;\n\n        for (int ic = 0; ic < 10; ic++)\n        {\n            Vec4f cs;\n\n            for (int iz = 0; iz < 4; iz++)\n            {\n                const Vec4f& ct0 = (Vec4f&) *datasetC[ic][iz][0];\n                const Vec4f& ct1 = (Vec4f&) *datasetC[ic][iz][1];\n\n                cs[iz] = dot(wt, lerp(ct0, ct1, albedo));\n            }\n\n            if (ic == 0)\n                rad = dot(ws, cs);\n            else\n                coeffs[ic - 1] = dot(ws, cs);\n        }\n\n        return rad;\n    }\n\n    // Hosek:\n    // (1 + A e ^ (B / cos(t))) (1 + C e ^ (D g) + E cos(g) ^ 2   + F mieM(g, G)  + H cos(t)^1/2 + (I - 1))\n    //\n    // These bits are the same as Preetham, but do different jobs in some cases\n    // A: sky gradient, carries white -> blue gradient\n    // B: sky tightness\n    // C: sun, carries most of sun-centred blue term\n    // D: sun tightness, higher = tighter\n    // E: rosy hue around sun\n    //\n    // Hosek-specific:\n    // F: mie term, does most of the heavy lifting for sunset glow\n    // G: mie tuning\n    // H: zenith gradient\n    // I: constant term balanced with H\n\n    // Notes:\n    // A/B still carries some of the \"blue\" base of sky, but much comes from C/D\n    // C/E minimal effect in sunset situations, carry bulk of sun halo in sun-overhead\n    // F/G sunset glow, but also takes sun halo from yellowish to white overhead\n\n    float EvalHosekCoeffs(const float coeffs[9], float cosTheta, float gamma, float cosGamma)\n    {\n        // Current coeffs ordering is AB I CDEF HG\n        //                            01 2 3456 78\n        const float expM   = expf(coeffs[4] * gamma);   // D g\n        const float rayM   = cosGamma * cosGamma;       // Rayleigh scattering\n        const float mieM   = (1.0f + rayM) / powf((1.0f + coeffs[8] * coeffs[8] - 2.0f * coeffs[8] * cosGamma), 1.5f);  // G\n        const float zenith = sqrtf(cosTheta);           // vertical zenith gradient\n\n        return (1.0f\n                     + coeffs[0] * expf(coeffs[1] / (cosTheta + 0.01f))     // A, B\n               )\n             * (1.0f\n                     + coeffs[3] * expM     // C\n                     + coeffs[5] * rayM     // E\n                     + coeffs[6] * mieM     // F\n                     + coeffs[7] * zenith   // H\n                     + (coeffs[2] - 1.0f)   // I\n               );\n    }\n}\n\n\nSkyHosek::SkyHosek() :\n    mToSun(vl_z)\n{\n}\n\nvoid SkyHosek::Update(const Vec3f& sun, float turbidity, Vec3f rgbAlbedo, float overcast)\n{\n    mToSun = sun;\n\n    float solarElevation = mToSun.z > 0.0f ? asinf(mToSun.z) : 0.0f;    // altitude rather than zenith, so sin rather than cos\n\n    mAlbedo = RGBToXYZ(rgbAlbedo);\n\n    // Note that the hosek coefficients change with time of day, vs. Preetham where the 'upper' coefficients stay the same,\n    // and only the scaler mPerezInvDen, consisting of time-dependent normalisation and zenith luminnce factors, changes.\n\n    if (!mUseCubic)\n    {\n        mRadXYZ[0] = FindHosekCoeffs(kHosekCoeffsX, kHosekRadX, turbidity, mAlbedo.x, solarElevation, mCoeffsXYZ[0]);\n        mRadXYZ[1] = FindHosekCoeffs(kHosekCoeffsY, kHosekRadY, turbidity, mAlbedo.y, solarElevation, mCoeffsXYZ[1]);\n        mRadXYZ[2] = FindHosekCoeffs(kHosekCoeffsZ, kHosekRadZ, turbidity, mAlbedo.z, solarElevation, mCoeffsXYZ[2]);\n    }\n    else\n    {\n        mRadXYZ[0] = FindHosekCoeffs(kHCX, turbidity, mAlbedo.x, solarElevation, mCoeffsXYZ[0]);\n        mRadXYZ[1] = FindHosekCoeffs(kHCY, turbidity, mAlbedo.y, solarElevation, mCoeffsXYZ[1]);\n        mRadXYZ[2] = FindHosekCoeffs(kHCZ, turbidity, mAlbedo.z, solarElevation, mCoeffsXYZ[2]);\n    }\n\n    mRadXYZ *= 683; // convert to luminance in lumens\n\n    if (mToSun.z < 0.0f)   // sun below horizon?\n    {\n        float s = ClampUnit(1.0f + mToSun.z * 50.0f);   // goes from 1 to 0 as the sun sets\n        float is = 1.0f - s;\n\n        // Emulate Preetham's zenith darkening\n        float darken = ZenithLuminance(acosf(mToSun.z), turbidity) / ZenithLuminance(vlf_halfPi, turbidity);\n\n        // Take C/E/F which control sun term to zero\n        for (int j = 0; j < 3; j++)\n        {\n            mCoeffsXYZ[j][3] *= s;\n            mCoeffsXYZ[j][5] *= s;\n            mCoeffsXYZ[j][6] *= s;\n\n            // Take horizon term H to zero, as it's an orange glow at this point\n            mCoeffsXYZ[j][7] *= s;\n\n            // Take I term back to 1\n            mCoeffsXYZ[j][2] *= s;\n            mCoeffsXYZ[j][2] += is;\n        }\n\n        mRadXYZ *= darken;\n    }\n\n    if (overcast != 0.0f)      // Handle overcast term\n    {\n        float is = overcast;\n        float s = 1.0f - overcast;     // goes to 0 as we go to overcast\n\n        // Hosek isn't self-normalising, unlike Preetham/CIE, which divides by PreethamLower().\n        // Thus when we lerp to the CIE overcast model, we get some non-linearities.\n        // We deal with this by using ratios of normalisation terms to balance.\n        // Another difference is that Hosek is relative to the average radiance,\n        // whereas CIE is the zenith radiance, so rather than taking the zenith\n        // as normalising as in CIE, we average over the zenith and two horizon\n        // points.\n        float cosGammaZ = mToSun.z;\n        float gammaZ    = acosf(cosGammaZ);\n        float cosGammaH = mToSun.y;\n        float gammaHP   = acosf(+mToSun.y);\n        float gammaHN   = vlf_pi - gammaHP;\n\n        float sc0 = EvalHosekCoeffs(mCoeffsXYZ[1], 1.0f, gammaZ,   cosGammaZ) * 2.0f\n                  + EvalHosekCoeffs(mCoeffsXYZ[1], 0.0f, gammaHP, +cosGammaH)\n                  + EvalHosekCoeffs(mCoeffsXYZ[1], 0.0f, gammaHN, -cosGammaH);\n\n        for (int j = 0; j < 3; j++)\n        {\n            // sun flare -> 0 strength/base chroma\n            // Take C/E/F which control sun term to zero\n            mCoeffsXYZ[j][3] *= s;\n            mCoeffsXYZ[j][5] *= s;\n            mCoeffsXYZ[j][6] *= s;\n\n            // Take H back to 0\n            mCoeffsXYZ[j][7] *= s;\n\n            // Take I term back to 1\n            mCoeffsXYZ[j][2] *= s;\n            mCoeffsXYZ[j][2] += is;\n\n            // Take A/B to  CIE cloudy sky model: 4, -0.7\n            mCoeffsXYZ[j][0] = lerp(mCoeffsXYZ[j][0],  4.0f, is);\n            mCoeffsXYZ[j][1] = lerp(mCoeffsXYZ[j][1], -0.7f, is);\n        }\n\n        float sc1 = EvalHosekCoeffs(mCoeffsXYZ[1], 1.0f, gammaZ,   cosGammaZ) * 2.0f\n                  + EvalHosekCoeffs(mCoeffsXYZ[1], 0.0f, gammaHP, +cosGammaH)\n                  + EvalHosekCoeffs(mCoeffsXYZ[1], 0.0f, gammaHN, -cosGammaH);\n\n        float rescale = sc0 / sc1;\n        mRadXYZ *= rescale;\n\n        // move back to white point\n        mRadXYZ.x = lerp(mRadXYZ.x, mRadXYZ.y, is);\n        mRadXYZ.z = lerp(mRadXYZ.z, mRadXYZ.y, is);\n    }\n\n#ifdef LOCAL_DEBUG\n    for (int j = 0; j < 3; j++)\n    {\n        printf(\"%c: Rad = %8.2f, C[9] = \", 'X' + j, mRadXYZ[j]);\n        for (int i = 0; i < 9; i++)\n            printf(\"%5.2f \", mCoeffsXYZ[j][i]);\n        printf(\"\\n\");\n    }\n#endif\n}\n\nfloat SkyHosek::SkyLuminance(const Vec3f& v) const\n{\n    float cosTheta = v.z;\n    float cosGamma = dot(mToSun, v);\n    float gamma    = acosf(cosGamma);\n\n    if (cosTheta < 0.0f)\n        cosTheta = 0.0f;\n\n    return EvalHosekCoeffs(mCoeffsXYZ[1], cosTheta, gamma, cosGamma) * mRadXYZ.y;\n}\n\nVec3f SkyHosek::SkyXYZ(const Vec3f& v) const\n{\n    float cosTheta = v.z;\n    float cosGamma = dot(mToSun, v);\n    float gamma    = acosf(cosGamma);\n\n    if (cosTheta < 0.0f)\n        cosTheta = 0.0f;\n\n    Vec3f XYZ =\n    {\n        EvalHosekCoeffs(mCoeffsXYZ[0], cosTheta, gamma, cosGamma),\n        EvalHosekCoeffs(mCoeffsXYZ[1], cosTheta, gamma, cosGamma),\n        EvalHosekCoeffs(mCoeffsXYZ[2], cosTheta, gamma, cosGamma)\n    };\n\n    XYZ *= mRadXYZ;\n\n    return XYZ;\n}\n\nVec3f SkyHosek::SkyRGB(const Vec3f& v) const\n{\n    return XYZToRGB(SkyXYZ(v));\n}\n\n\n//------------------------------------------------------------------------------\n// SkyTable\n//------------------------------------------------------------------------------\n\n// Pre-calculated table version, particularly useful for shader use\n\nnamespace\n{\n#ifdef REMAP_THETA\n    // remap cos theta to concentrate table around horizon\n    inline float MapTheta(float t)\n    {\n        return t >= 0.0f ? sqrtf(t) : -sqrtf(-t);\n    }\n    inline float UnmapTheta(float t)\n    {\n        return t >= 0.0f ? sqr(t) : -sqr(t);\n    }\n    inline float UnmapThetaWeight(float t)\n    {\n        return 2 * fabsf(t);\n    }\n#else\n    inline float MapTheta(float t)\n    {\n        return t;\n    }\n    inline float UnmapTheta(float t)\n    {\n        return t;\n    }\n    inline float UnmapThetaWeight(float t)\n    {\n        return 1.0f;\n    }\n#endif\n\n    // remap cosGamma to concentrate table around the sun location\n    inline float MapGamma(float g)\n    {\n        return sqrtf(0.5f * (1.0f - g));\n    }\n    inline float UnmapGamma(float g)\n    {\n        return 1 - 2 * sqr(g);\n    }\n    inline float UnmapGammaWeight(float g)\n    {\n        // |UnmapGamma'|\n        return 4 * g;\n    }\n}\n\nvoid SkyTable::FindThetaGammaTables(const SkyPreetham& pt)\n{\n    float dt = 1.0f / (kTableSize - 1);\n    float t = dt * 1e-6f;    // epsilon to avoid divide by 0, which can lead to NaN when m_perez_[1] = 0\n\n    for (int i = 0; i < kTableSize; i++)\n    {\n        float cosTheta = UnmapTheta(t);\n\n        mThetaTable[i][0] =  -pt.mPerez_x[0] * expf(pt.mPerez_x[1] / cosTheta);\n        mThetaTable[i][1] =  -pt.mPerez_y[0] * expf(pt.mPerez_y[1] / cosTheta);\n        mThetaTable[i][2] =  -pt.mPerez_Y[0] * expf(pt.mPerez_Y[1] / cosTheta);\n\n        mMaxTheta = mMaxTheta >= mThetaTable[i][2] ? mMaxTheta : mThetaTable[i][2];\n\n        float cosGamma = UnmapGamma(t);\n        float gamma = acosf(cosGamma);\n\n        mGammaTable[i][0] =  pt.mPerez_x[2] * expf(pt.mPerez_x[3] * gamma) + pt.mPerez_x[4] * sqr(cosGamma);\n        mGammaTable[i][1] =  pt.mPerez_y[2] * expf(pt.mPerez_y[3] * gamma) + pt.mPerez_y[4] * sqr(cosGamma);\n        mGammaTable[i][2] =  pt.mPerez_Y[2] * expf(pt.mPerez_Y[3] * gamma) + pt.mPerez_Y[4] * sqr(cosGamma);\n\n        mMaxGamma = mMaxGamma >= mGammaTable[i][2] ? mMaxGamma : mGammaTable[i][2];\n\n        t += dt;\n    }\n\n    mXYZ = false;\n\n#ifdef SUPPORT_OVERCAST_CLAMP\n    mMaxTheta = -pt.mPerez_Y[0] * expf(pt.mPerez_Y[1]);\n#endif\n\n#ifdef SIM_CLAMP\n    for (int i = 0; i < kTableSize; i++)\n    {\n        mThetaTable[i].z /= mMaxTheta;\n        mGammaTable[i].z /= mMaxGamma;\n        mThetaTable[i] = ClampUnit(mThetaTable[i]);\n        mGammaTable[i] = ClampUnit(mGammaTable[i]);\n    }\n#endif\n\n#ifdef LOCAL_DEBUG\n    printf(\"PTx: \");\n    for (int i = 0; i < kTableSize; i += 4)\n        printf(\"%5.2f \", mThetaTable[i].x);\n    printf(\"\\n\");\n    printf(\"PTy: \");\n    for (int i = 0; i < kTableSize; i += 4)\n        printf(\"%5.2f \", mThetaTable[i].y);\n    printf(\"\\n\");\n    printf(\"PTY: \");\n    for (int i = 0; i < kTableSize; i += 4)\n        printf(\"%5.2f \", mThetaTable[i].z);\n    printf(\"\\n\");\n\n    printf(\"PGx: \");\n    for (int i = 0; i < kTableSize; i += 4)\n        printf(\"%5.2f \", mGammaTable[i].x);\n    printf(\"\\n\");\n    printf(\"PGy: \");\n    for (int i = 0; i < kTableSize; i += 4)\n        printf(\"%5.2f \", mGammaTable[i].y);\n    printf(\"\\n\");\n    printf(\"PGY: \");\n    for (int i = 0; i < kTableSize; i += 4)\n        printf(\"%5.2f \", mGammaTable[i].z);\n    printf(\"\\n\");\n#endif\n}\n\nvoid SkyTable::FindThetaGammaTables(const SkyHosek& hk)\n{\n    const float (&coeffsXYZ)[3][9] = hk.mCoeffsXYZ;\n\n    mMaxGamma = 1.0f;\n\n    float dt = 1.0f / (kTableSize - 1);\n    float t = 0.0f;\n\n    for (int i = 0; i < kTableSize; i++)\n    {\n        float cosTheta = UnmapTheta(t);\n        float cosGamma = UnmapGamma(t);\n        float gamma = acosf(cosGamma);\n\n        float rayM = cosGamma * cosGamma;\n\n        for (int j = 0; j < 3; j++)\n        {\n            const float (&coeffs)[9] = coeffsXYZ[j];\n\n            mThetaTable[i][j] = -coeffs[0] * expf(coeffs[1] / (cosTheta + 0.01f));\n\n            float expM   = expf(coeffs[4] * gamma);\n            float mieM   = (1.0f + rayM) / powf((1.0f + coeffs[8] * coeffs[8] - 2.0f * coeffs[8] * cosGamma), 1.5f);\n\n            mGammaTable[i][j] = coeffs[3] * expM + coeffs[5] * rayM + coeffs[6] * mieM;\n\n            mMaxGamma = mMaxGamma >= mGammaTable[i][j] ? mMaxGamma : mGammaTable[i][j];\n        }\n\n        t += dt;\n    }\n\n    mXYZ = true;\n\n#ifdef SUPPORT_OVERCAST_CLAMP\n    mMaxTheta = -coeffsXYZ[1][0] * expf(coeffsXYZ[1][1]);\n#endif\n\n#ifdef SIM_CLAMP\n    for (int i = 0; i < kTableSize; i++)\n    {\n        mThetaTable[i] = ClampUnit(mThetaTable[i] / mMaxTheta);\n        mGammaTable[i] = ClampUnit(mGammaTable[i] / mMaxGamma);\n    }\n#endif\n\n#ifdef LOCAL_DEBUG\n    printf(\"HTY: \");\n    for (int i = 0; i < kTableSize; i += 4)\n        printf(\"%5.2f \", mThetaTable[i].x);\n    printf(\"\\n\");\n    printf(\"HTY: \");\n    for (int i = 0; i < kTableSize; i += 4)\n        printf(\"%5.2f \", mThetaTable[i].y);\n    printf(\"\\n\");\n    printf(\"HTY: \");\n    for (int i = 0; i < kTableSize; i += 4)\n        printf(\"%5.2f \", mThetaTable[i].z);\n    printf(\"\\n\");\n\n    printf(\"HGX: \");\n    for (int i = 0; i < kTableSize; i += 4)\n        printf(\"%5.2f \", mGammaTable[i].x);\n    printf(\"\\n\");\n    printf(\"HGY: \");\n    for (int i = 0; i < kTableSize; i += 4)\n        printf(\"%5.2f \", mGammaTable[i].y);\n    printf(\"\\n\");\n    printf(\"HGZ: \");\n    for (int i = 0; i < kTableSize; i += 4)\n        printf(\"%5.2f \", mGammaTable[i].z);\n    printf(\"\\n\");\n#endif\n}\n\nVec3f SkyTable::SkyRGB(const SkyPreetham& pt, const Vec3f& v) const\n{\n    float cosTheta = v.z;\n    float cosGamma = dot(pt.mToSun, v);\n\n    if (cosTheta < 0.0f)\n        cosTheta = 0.0f;\n\n    float t = MapTheta(cosTheta);\n    float g = MapGamma(ClampUnit(cosGamma));\n\n    Vec3f F = LerpSample(t, kTableSize, mThetaTable);\n    Vec3f G = LerpSample(g, kTableSize, mGammaTable);\n\n#ifdef SIM_CLAMP\n    F.z *= mMaxTheta;\n    G.z *= mMaxGamma;\n#endif\n\n    Vec3f xyY = (Vec3f(vl_1) - F) * (Vec3f(vl_1) + G);\n\n    xyY *= pt.mPerezInvDen;\n\n    return xyYToRGB(xyY);\n}\n\nVec3f SkyTable::SkyRGB(const SkyHosek& hk, const Vec3f& v) const\n{\n    float cosTheta = v.z;\n    float cosGamma = dot(hk.mToSun, v);\n\n    if (cosTheta < 0.0f)\n        cosTheta = 0.0f;\n\n    float t = MapTheta(cosTheta);\n    float g = MapGamma(cosGamma);\n\n    Vec3f F = LerpSample(t, kTableSize, mThetaTable);\n    Vec3f G = LerpSample(g, kTableSize, mGammaTable);\n\n    const float zenith = sqrtf(cosTheta);\n    Vec3f H\n    (\n        hk.mCoeffsXYZ[0][7] * zenith + (hk.mCoeffsXYZ[0][2] - 1.0f),\n        hk.mCoeffsXYZ[1][7] * zenith + (hk.mCoeffsXYZ[1][2] - 1.0f),\n        hk.mCoeffsXYZ[2][7] * zenith + (hk.mCoeffsXYZ[2][2] - 1.0f)\n    );\n\n#ifdef SIM_CLAMP\n    F *= mMaxTheta;\n    G *= mMaxGamma;\n#endif\n\n    // (1 - F(theta)) * (1 + G(phi) + H(theta))\n    Vec3f XYZ = (Vec3f(vl_1) - F) * (Vec3f(vl_1) + G + H);\n\n    XYZ *= hk.mRadXYZ;\n\n    return XYZToRGB(XYZ);\n}\n\nvoid SkyTable::FillTexture(int width, int height, uint8_t image[][4]) const\n{\n    VL_ASSERT(width == kTableSize);\n    VL_ASSERT(height == 2);\n\n    (void) width;\n    (void) height;\n\n    for (int i = 0; i < width; i++)\n    {\n        Vec3f c = mThetaTable[i];\n\n        if (mXYZ)\n            c /= mMaxTheta;\n        else\n            c.z /= mMaxTheta;\n\n        image[i][2] = ToU8(c.x);\n        image[i][1] = ToU8(c.y);\n        image[i][0] = ToU8(c.z);\n        image[i][3] = 255;\n    }\n\n    image += width;\n\n    for (int i = 0; i < width; i++)\n    {\n        Vec3f c = mGammaTable[i];\n\n        if (mXYZ)\n            c /= mMaxGamma;\n        else\n            c.z /= mMaxGamma;\n\n        image[i][2] = ToU8(c.x);\n        image[i][1] = ToU8(c.y);\n        image[i][0] = ToU8(c.z);\n        image[i][3] = 255;\n    }\n}\n\nvoid SkyTable::FillTexture(int width, int height, float image[][4]) const\n{\n    VL_ASSERT(width == kTableSize);\n    VL_ASSERT(height == 2);\n\n    (void) width;\n    (void) height;\n\n    for (int i = 0; i < width; i++)\n        *(Vec4f*) image[i] = Vec4f(mThetaTable[i], 1.0f);\n\n    image += width;\n\n    for (int i = 0; i < width; i++)\n        *(Vec4f*) image[i] = Vec4f(mGammaTable[i], 1.0f);\n}\n\n//------------------------------------------------------------------------------\n// SkyBRDF\n//------------------------------------------------------------------------------\n\n// Extended version that enables lerping between perfect mirror and fully\n// diffuse.\n\nnamespace\n{\n    // ZH routines from SHLib\n    const float kZH_Y_0 = sqrtf( 1 / (   4 * vlf_pi));  //         1\n    const float kZH_Y_1 = sqrtf( 3 / (   4 * vlf_pi));  //         z\n    const float kZH_Y_2 = sqrtf( 5 / (  16 * vlf_pi));  // 1/2     (3 z^2 - 1)\n    const float kZH_Y_3 = sqrtf( 7 / (  16 * vlf_pi));  // 1/2     (5 z^3 - 3 z)\n    const float kZH_Y_4 = sqrtf( 9 / ( 256 * vlf_pi));  // 1/8     (35 z^4 - 30 z^2 + 3)\n    const float kZH_Y_5 = sqrtf(11 / ( 256 * vlf_pi));  // 1/8     (63 z^5 - 70 z^3 + 15 z)\n    const float kZH_Y_6 = sqrtf(13 / (1024 * vlf_pi));  // 1/16    (231 z^6 - 315 z^4 + 105 z^2 - 5)\n\n    void CalcCosPowerSatZH7(float n, float zcoeffs[7])\n    {\n        zcoeffs[0] =   1.0f / (n + 1);\n        zcoeffs[1] =   1.0f / (n + 2);\n        zcoeffs[2] =   3.0f / (n + 3) -   1.0f / (n + 1);\n        zcoeffs[3] =   5.0f / (n + 4) -   3.0f / (n + 2);\n        zcoeffs[4] =  35.0f / (n + 5) -  30.0f / (n + 3) +   3.0f / (n + 1);\n        zcoeffs[5] =  63.0f / (n + 6) -  70.0f / (n + 4) +  15.0f / (n + 2);\n        zcoeffs[6] = 231.0f / (n + 7) - 315.0f / (n + 5) + 105.0f / (n + 3) - 5.0f / (n + 1);\n\n        // apply norm constants\n        zcoeffs[0] *= vlf_twoPi * kZH_Y_0;\n        zcoeffs[1] *= vlf_twoPi * kZH_Y_1;\n        zcoeffs[2] *= vlf_twoPi * kZH_Y_2;\n        zcoeffs[3] *= vlf_twoPi * kZH_Y_3;\n        zcoeffs[4] *= vlf_twoPi * kZH_Y_4;\n        zcoeffs[5] *= vlf_twoPi * kZH_Y_5;\n        zcoeffs[6] *= vlf_twoPi * kZH_Y_6;\n\n        // [0]: 2pi sqrtf( 1 / (   4 * vlf_pi)) / 1\n        // we'll multiply by alpha = sqrtf(4.0f * vlf_pi / (2 * i + 1)) in convolution, leaving 2pi.\n    }\n\n    void CalcZH7Weights(float z, float w[7])\n    {\n        float z2 = z * z;\n        float z3 = z2 * z;\n        float z4 = z2 * z2;\n        float z5 = z2 * z3;\n        float z6 = z3 * z3;\n\n        w[0] = kZH_Y_0;\n        w[1] = kZH_Y_1 * z;\n        w[2] = kZH_Y_2 * (3 * z2 - 1);\n        w[3] = kZH_Y_3 * (5 * z3 - 3 * z);\n        w[4] = kZH_Y_4 * (35 * z4 - 30 * z2 + 3);\n        w[5] = kZH_Y_5 * (63 * z5 - 70 * z3 + 15 * z);\n        w[6] = kZH_Y_6 * (231 * z6 - 315 * z4 + 105 * z2 - 5);\n    }\n\n    inline float WindowScale(int n, float gamma)\n    {\n        float nt = float(n * (n + 1));\n        return 1.0f / (1.0f + gamma * nt * nt);\n    }\n    // End ZH routines from SHLib\n\n    template<class T> T SampleZH7(float z, const T zhCoeffs[7])\n    {\n        float weights[7];\n        CalcZH7Weights(z, weights);\n\n        T c = zhCoeffs[0] * weights[0];\n\n        for (int i = 1; i < 7; i++)\n            c += zhCoeffs[i] * weights[i];\n\n        return c;\n    }\n\n    template<class T> void AddZH7Sample(float z, T c, T zhCoeffs[7])\n    {\n        float weights[7];\n        CalcZH7Weights(z, weights);\n\n        for (int i = 0; i < 7; i++)\n            zhCoeffs[i] += c * weights[i];\n    }\n\n    template<class T> void ApplyZH7Windowing(float gamma, T coeffs[7])\n    {\n        for (int i = 0; i < 7; i++)\n            coeffs[i] *= WindowScale(i, gamma);\n    }\n\n    // ConvolveZHWithZH with Vec3 and divides result through by the first coefficient.\n    template<class T> void ConvolveZH7WithZH7Norm(const float brdfCoeffs[7], const T zhCoeffsIn[7], T zhCoeffsOut[7])\n    {\n        zhCoeffsOut[0] = zhCoeffsIn[0];\n\n        for (int i = 1; i < 7; i++)\n        {\n            float invAlpha = sqrtf(2.0f * i + 1);\n\n            zhCoeffsOut[i] = zhCoeffsIn[i] * (brdfCoeffs[i] / (invAlpha * brdfCoeffs[0]));\n        }\n    }\n\n    template<class T> void FindZH7FromThetaTable(int tableSize, const T table[], T zhCoeffs[7])\n    {\n        float dt = 1.0f / (tableSize - 1);\n        float t = 0.0f;\n\n        for (int i = 0; i < 7; i++)\n            zhCoeffs[i] = vl_0;\n\n        for (int i = 0; i < tableSize; i++)\n        {\n            float tt = 2 * t - 1;\n            float z = UnmapTheta(tt);\n            float dz = vlf_twoPi * UnmapThetaWeight(tt) * (2.0f * dt);\n            AddZH7Sample(z, table[i] * dz, zhCoeffs);\n\n            t += dt;\n        }\n    }\n\n    template<class T> void GenerateThetaTableFromZH7(const T zhCoeffs[7], int tableSize, T table[])\n    {\n        float dt = 1.0f / (tableSize - 1);\n        float t = 0.0f;\n\n        for (int i = 0; i < tableSize; i++)\n        {\n            table[i] = SampleZH7(UnmapTheta(2 * t - 1), zhCoeffs);\n\n            t += dt;\n        }\n    }\n\n    template<class T> void FindZH7FromGammaTable(int tableSize, const T table[], T zhCoeffs[7])\n    {\n        float dg = 1.0f / (tableSize - 1);\n        float g = 0.0f;\n\n        for (int i = 0; i < 7; i++)\n            zhCoeffs[i] = vl_0;\n\n        for (int i = 0; i < tableSize; i++)\n        {\n            // Works better for gamma to effectively importance-sample sun area\n            float z = UnmapGamma(g);\n            float dz = vlf_twoPi * UnmapGammaWeight(g) * dg;\n\n            AddZH7Sample(z, table[i] * dz, zhCoeffs);\n\n            g += dg;\n        }\n    }\n\n    template<class T> void GenerateGammaTableFromZH7(T zhCoeffs[7], int tableSize, T table[])\n    {\n        float dg = 1.0f / (tableSize - 1);\n        float g = 0.0f;\n\n        for (int i = 0; i < tableSize; i++)\n        {\n            table[i] = SampleZH7(UnmapGamma(g), zhCoeffs);\n            g += dg;\n        }\n    }\n\n    inline Vec3f Bias_xyY(Vec3f c)  // effectively make delta lum proportional to real lum, and scale xy by lum\n    {\n        c.z += 1.0f;\n        c.x *= c.z;\n        c.y *= c.z;\n        return c;\n    }\n\n    inline Vec3f Unbias_xyY(Vec3f c)  // Return to delta xyY form\n    {\n        float y = c.z;\n        if (y < 1e-2f)\n            y = 1e-2f;\n\n        c.x /= y;\n        c.y /= y;\n        c.z -= 1.0f;\n        return c;\n    }\n\n    const float kThetaW = 0.01f;    // windowing gamma to use for theta table\n    const float kGammaW = 0.002f;   // windowing gamma to use for gamma table\n\n    float kThetaWHosek  = 0.11f;   // windowing gamma to use for Hosek theta table\n    float kGammaWHosek  = 0.002f;  // windowing gamma to use for Hosek gamma table\n    float kThetaWHosekH = 0.01f;   // windowing gamma to use for Hosek theta/H table\n\n    constexpr float RowPower(float i, float n)\n    {\n        // use N = 2 / r^2 - 2, and then +1 for base cos power\n        // float r = i / (n - 1);  requires C++14, amazing\n        // return 2.0f / (r * r + 1e-8f) - 1.0f;\n        return 2.0f / ((i / (n - 1)) * (i / (n - 1)) + 1e-8f) - 1.0f;\n    }\n\n#ifdef COMPACT_BRDF_TABLE\n    const float kRowPowers[SkyBRDF::kBRDFSamples] = { RowPower(0, 4), RowPower(1, 4), RowPower(2, 4), RowPower(3, 4) };\n#else\n    const float kRowPowers[SkyBRDF::kBRDFSamples] = { RowPower(0, 8), RowPower(1, 8), RowPower(2, 8), RowPower(3, 8), RowPower(4, 8), RowPower(5, 8), RowPower(6, 8), RowPower(7, 8) };\n#endif\n}\n\nvoid SkyBRDF::FindBRDFTables(const SkyTable& table, const SkyPreetham&)\n{\n    // The BRDF tables cover the entire sphere, so we must resample theta from the Perez/Hosek tables which cover a hemisphere.\n    Vec3f thetaTable[kTableSize];\n    const Vec3f* gammaTable = table.mGammaTable;\n\n    // Fill top hemisphere of table\n    for (int i = 0; i < kHalfTableSize; i++)\n        thetaTable[kHalfTableSize + i] = table.mThetaTable[2 * i];\n\n    // Fill lower hemisphere with term that evaluates close to 0, to avoid below-ground luminance leaking in.\n    // TODO: modify this using ground albedo\n    Vec3f lowerHemi = Vec3f(0.0f, 0.0f, 0.999f);  // 0.999 as theta table is used as (1 - F)\n\n    for (int i = 0; i < kHalfTableSize; i++)\n        thetaTable[i] = lowerHemi;\n\n    // Project tables into ZH coefficients\n    Vec3f zhCoeffsTheta[7];\n    Vec3f zhCoeffsGamma[7];\n\n    // theta table works better if we operate on something proportional to real luminance\n    Vec3f biasedThetaTable[kTableSize];\n    for (int i = 0; i < kTableSize; i++)\n        biasedThetaTable[i] = Bias_xyY(thetaTable[i]);\n\n    FindZH7FromThetaTable(kTableSize, biasedThetaTable, zhCoeffsTheta);\n    FindZH7FromGammaTable(kTableSize,       gammaTable, zhCoeffsGamma);\n\n    ApplyZH7Windowing(kThetaW, zhCoeffsTheta);\n    ApplyZH7Windowing(kGammaW, zhCoeffsGamma);\n\n    // row 0 is the original unconvolved signal\n\n    // Firstly, fill -ve z with reflected +ve z, ramped to 0 at z = -1. This avoids discontinuities at the horizon.\n    for (int i = 0; i < kHalfTableSize; i++)\n    {\n        thetaTable[i] = table.mThetaTable[kTableSize - 1 - 2 * i];\n\n        // Ramp luminance down\n        float s = (i + 0.5f) / kHalfTableSize;\n        s = sqrtf(s);\n\n        thetaTable[i].z = thetaTable[i].z * s + (1 - s);\n    }\n\n    for (int i = 0; i < kTableSize; i++)\n    {\n        mBRDFThetaTable[0][i] = thetaTable[i];\n        mBRDFGammaTable[0][i] = gammaTable[i];\n    }\n\n    // rows 1..n-1 are successive convolutions\n    Vec3f zhCoeffsThetaConv[7];\n    Vec3f zhCoeffsGammaConv[7];\n\n    for (int r = 1; r < kBRDFSamples; r++)\n    {\n        float s = kRowPowers[r];\n\n        float csCoeffs[7];\n        CalcCosPowerSatZH7(s, csCoeffs);\n\n        ConvolveZH7WithZH7Norm(csCoeffs, zhCoeffsTheta, zhCoeffsThetaConv);\n        ConvolveZH7WithZH7Norm(csCoeffs, zhCoeffsGamma, zhCoeffsGammaConv);\n\n        GenerateThetaTableFromZH7(zhCoeffsThetaConv, kTableSize, mBRDFThetaTable[r]);\n        GenerateGammaTableFromZH7(zhCoeffsGammaConv, kTableSize, mBRDFGammaTable[r]);\n\n        for (int i = 0; i < kTableSize; i++)\n            mBRDFThetaTable[r][i] = Unbias_xyY(mBRDFThetaTable[r][i]);\n    }\n\n    mMaxTheta = table.mMaxTheta;\n    mMaxGamma = table.mMaxGamma;\n    mXYZ = false;\n    mHasHTerm = false;\n\n#ifdef SIM_CLAMP\n    for (int r = 1; r < kBRDFSamples; r++)\n        for (int i = 0; i < kTableSize; i++)\n        {\n            mBRDFThetaTable  [r][i] = ClampUnit(mBRDFThetaTable  [r][i]);\n            mBRDFGammaTable  [r][i] = ClampUnit(mBRDFGammaTable  [r][i]);\n        }\n#endif\n}\n\nvoid SkyBRDF::FindBRDFTables(const SkyTable& table, const SkyHosek& hk)\n{\n    // The BRDF tables cover the entire sphere, so we must resample theta from the Perez/Hosek tables which cover a hemisphere.\n    Vec3f thetaTable[SkyTable::kTableSize];\n    const Vec3f* gammaTable = table.mGammaTable;\n\n    // Fill top hemisphere of table\n    for (int i = 0; i < kHalfTableSize; i++)\n        thetaTable[kHalfTableSize + i] = table.mThetaTable[2 * i];\n\n    // Fill lower hemisphere with albedo-weighted sky.\n\n    for (int i = 0; i < kHalfTableSize; i++)\n    {\n        thetaTable[i] = table.mThetaTable[kTableSize - 1 - 2 * i];\n        // Table is F, C = (1 - F)\n        thetaTable[i] = Vec3f(vl_1) - hk.mAlbedo * (Vec3f(vl_1) - thetaTable[i]);\n    }\n\n    // Project tables into ZH coefficients\n    Vec3f zhCoeffsTheta[7];\n    Vec3f zhCoeffsGamma[7];\n\n    // theta table works better if we operate on something proportional to real luminance\n    Vec3f biasedThetaTable[kTableSize];\n\n    for (int i = 0; i < kTableSize; i++)\n        biasedThetaTable[i] = thetaTable[i] + vl_one;\n\n    FindZH7FromThetaTable(kTableSize, biasedThetaTable, zhCoeffsTheta);\n    FindZH7FromGammaTable(kTableSize,       gammaTable, zhCoeffsGamma);\n\n    ApplyZH7Windowing(kThetaWHosek, zhCoeffsTheta);\n    ApplyZH7Windowing(kGammaWHosek, zhCoeffsGamma);\n\n    // row 0 is the original unconvolved signal, just copy it\n    for (int i = 0; i < kTableSize; i++)\n    {\n        mBRDFThetaTable[0][i] = thetaTable[i];\n        mBRDFGammaTable[0][i] = gammaTable[i];\n    }\n\n    // Construct H term table -- just the zenith part as we can potentially store as 4th component\n    for (int i = 0; i < kHalfTableSize; i++)\n    {\n        float cosTheta = UnmapTheta(i / float(kHalfTableSize - 1));\n        float zenith = sqrtf(cosTheta);\n\n        mBRDFThetaTableH[0][kHalfTableSize + i   ] = zenith;\n        mBRDFThetaTableH[0][kHalfTableSize -1 - i] = hk.mAlbedo.y * zenith;  // see mirror comment above\n    }\n\n    // Calculate FH as we get slightly better results convolving the full term\n    // rather than approximating, at the cost of an extra table\n    for (int i = 0; i < kTableSize; i++)\n        mBRDFThetaTableFH[0][i] = mBRDFThetaTableH[0][i] * thetaTable[i];\n\n    float zhCoeffsH[7];\n    Vec3f zhCoeffsFH[7];\n\n    FindZH7FromThetaTable(kTableSize, mBRDFThetaTableH [0], zhCoeffsH);\n    FindZH7FromThetaTable(kTableSize, mBRDFThetaTableFH[0], zhCoeffsFH);\n\n    ApplyZH7Windowing(kThetaWHosekH, zhCoeffsH);\n    ApplyZH7Windowing(kThetaWHosekH, zhCoeffsFH);\n\n    // Rows 1..n-1 are successive convolutions\n\n    for (int r = 1; r < kBRDFSamples; r++)\n    {\n        Vec3f zhCoeffsThetaConv  [7];\n        Vec3f zhCoeffsGammaConv  [7];\n        float zhCoeffsThetaConvH [7];\n        Vec3f zhCoeffsThetaConvFH[7];\n\n        // Scale up to full windowing at full spec power...\n        float rw = sqrtf(1.0f - r / (kBRDFSamples - 1));\n\n        float s = kRowPowers[r];\n\n        float csCoeffs[7];\n        CalcCosPowerSatZH7(s, csCoeffs);\n\n        ConvolveZH7WithZH7Norm(csCoeffs, zhCoeffsTheta, zhCoeffsThetaConv);\n        ConvolveZH7WithZH7Norm(csCoeffs, zhCoeffsGamma, zhCoeffsGammaConv);\n        ConvolveZH7WithZH7Norm(csCoeffs, zhCoeffsH    , zhCoeffsThetaConvH);\n        ConvolveZH7WithZH7Norm(csCoeffs, zhCoeffsFH   , zhCoeffsThetaConvFH);\n\n        // Generate convolved tables from ZH\n        GenerateThetaTableFromZH7(zhCoeffsThetaConv,   kTableSize, mBRDFThetaTable  [r]);\n        GenerateGammaTableFromZH7(zhCoeffsGammaConv,   kTableSize, mBRDFGammaTable  [r]);\n        GenerateThetaTableFromZH7(zhCoeffsThetaConvH,  kTableSize, mBRDFThetaTableH [r]);\n        GenerateThetaTableFromZH7(zhCoeffsThetaConvFH, kTableSize, mBRDFThetaTableFH[r]);\n\n        for (int i = 0; i < kTableSize; i++)\n        {\n            mBRDFThetaTable[r][i] -= vl_one;\n\n        #ifdef HOSEK_G_FIX\n            // Ringing on the Hosek G term leads to blue spots opposite the sun\n            // in sunset situatons. Trying to solve this completely via windowing\n            // leads to excessive blurring, so we also compensate by scaling down\n            // the far pole in this situation.\n            float g = i / float(kTableSize - 1);\n            float cosGamma = UnmapGamma(g);\n            if (cosGamma < -0.6f)\n                mBRDFGammaTable[r][i] *= ClampUnit(1.0f - sqr(-0.6f - cosGamma) * 1.5f * rw);\n        #endif\n        }\n    }\n\n    mMaxTheta = table.mMaxTheta;\n    mMaxGamma = table.mMaxGamma;\n    mXYZ = true;\n    mHasHTerm = true;\n\n#ifdef SIM_CLAMP\n    for (int r = 1; r < kBRDFSamples; r++)\n        for (int i = 0; i < kTableSize; i++)\n        {\n            mBRDFThetaTable  [r][i] = ClampUnit(mBRDFThetaTable  [r][i]);\n            mBRDFGammaTable  [r][i] = ClampUnit(mBRDFGammaTable  [r][i]);\n            mBRDFThetaTableH [r][i] = ClampUnit(mBRDFThetaTableH [r][i]);\n            mBRDFThetaTableFH[r][i] = ClampUnit(mBRDFThetaTableFH[r][i]);\n        }\n#endif\n}\n\nVec3f SkyBRDF::ConvolvedSkyRGB(const SkyPreetham& pt, const Vec3f& v, float r) const\n{\n    VL_ASSERT(!mXYZ);\n\n    float cosTheta = v.z;\n    float cosGamma = dot(pt.mToSun, v);\n\n    float t = 0.5f * (MapTheta(cosTheta) + 1);\n    float g = MapGamma(ClampUnit(cosGamma));\n\n    Vec3f F = BiLerpSample(t, r, kTableSize, kBRDFSamples, mBRDFThetaTable[0]);\n    Vec3f G = BiLerpSample(g, r, kTableSize, kBRDFSamples, mBRDFGammaTable[0]);\n\n#ifdef SIM_CLAMP\n    F.z *= mMaxTheta;\n    G.z *= mMaxGamma;\n#endif\n\n    // (1 - F(theta)) * (1 + G(phi))\n    Vec3f xyY = (Vec3f(vl_1) - F) * (Vec3f(vl_1) + G);\n\n    xyY *= pt.mPerezInvDen;\n\n    return xyYToRGB(xyY);\n}\n\nVec3f SkyBRDF::ConvolvedSkyRGB(const SkyHosek& hk, const Vec3f& v, float r) const\n{\n    VL_ASSERT(mXYZ);\n\n    float cosTheta = v.z;\n    float cosGamma = dot(hk.mToSun, v);\n\n    float t = 0.5f * (MapTheta(cosTheta) + 1);\n    float g = MapGamma(ClampUnit(cosGamma));\n\n    Vec3f F = BiLerpSample(t, r, kTableSize, kBRDFSamples, mBRDFThetaTable[0]);\n    Vec3f G = BiLerpSample(g, r, kTableSize, kBRDFSamples, mBRDFGammaTable[0]);\n\n#ifdef SIM_CLAMP\n    F *= mMaxTheta;\n    G *= mMaxGamma;\n#endif\n\n    Vec3f cH(hk.mCoeffsXYZ[0][7], hk.mCoeffsXYZ[1][7], hk.mCoeffsXYZ[2][7]);\n    Vec3f cI(hk.mCoeffsXYZ[0][2], hk.mCoeffsXYZ[1][2], hk.mCoeffsXYZ[2][2]);\n    cI -= vl_1;\n\n#ifdef HOSEK_BRDF_ANALYTIC_H\n    // Evaluate H term on the fly, for cross-checking.\n    // This could be done in shader to avoid the additional table lookup,\n    // but it's a fair bit of math, and using the table variant ensures better\n    // consistency with the F/G parts.\n    float zhZ[7];\n    CalcCosPowerSatZH7(0.5f, zhZ);\n\n    float zhR[7];\n    float n = LerpSample(r, kBRDFSamples, kRowPowers);\n    CalcCosPowerSatZH7(n, zhR);\n\n    float zhZR[7];\n    ConvolveZH7WithZH7Norm(zhR, zhZ, zhZR);\n\n    float zenith = SampleZH7(cosTheta, zhZR);\n\n    Vec3f H = zenith * cH + cI;\n    Vec3f FH = F * H;\n#else\n    Vec3f H  = BiLerpSample(t, r, kTableSize, kBRDFSamples, mBRDFThetaTableH [0]) * cH;\n    Vec3f FH = BiLerpSample(t, r, kTableSize, kBRDFSamples, mBRDFThetaTableFH[0]) * cH;\n\n#ifdef SIM_CLAMP\n    FH *= mMaxTheta;\n#endif\n\n    H  +=     cI;\n    FH += F * cI;\n#endif\n\n    // (1 - F(theta)) * (1 + G(phi) + H(theta))\n    Vec3f XYZ = (Vec3f(vl_1) - F) * (Vec3f(vl_1) + G) + H - FH;\n\n    XYZ = ClampPositive3(XYZ);\n    XYZ *= hk.mRadXYZ;\n\n    return XYZToRGB(XYZ);\n}\n\nvoid SkyBRDF::FillBRDFTexture(int width, int height, uint8_t image[][4]) const\n{\n    VL_ASSERT(width == kTableSize);\n    VL_ASSERT((height == 2 * kBRDFSamples) || (mHasHTerm && height >= 3 * kBRDFSamples));\n\n    for (int j = 0; j < kBRDFSamples; j++)\n    {\n        for (int i = 0; i < width; i++)\n        {\n            Vec3f c = mBRDFThetaTable[j][i];\n\n            if (mXYZ)\n                c /= mMaxTheta;\n            else\n                c.z /= mMaxTheta;\n\n            image[i][2] = ToU8(c.x);\n            image[i][1] = ToU8(c.y);\n            image[i][0] = ToU8(c.z);\n\n            if (mHasHTerm)\n                image[i][3] = ToU8(mBRDFThetaTableH[j][i]);\n            else\n                image[i][3] = 255;\n        }\n\n        image += width;\n    }\n\n    for (int j = 0; j < kBRDFSamples; j++)\n    {\n        for (int i = 0; i < width; i++)\n        {\n            Vec3f c = mBRDFGammaTable[j][i];\n\n            if (mXYZ)\n                c /= mMaxGamma;\n            else\n                c.z /= mMaxGamma;\n\n            image[i][2] = ToU8(c.x);\n            image[i][1] = ToU8(c.y);\n            image[i][0] = ToU8(c.z);\n            image[i][3] = 255;\n        }\n\n        image += width;\n    }\n\n    if (height < 3 * kBRDFSamples)\n        return;\n\n    // Want the H term too.\n    for (int j = 0; j < kBRDFSamples; j++)\n    {\n        for (int i = 0; i < width; i++)\n        {\n            Vec3f c = mBRDFThetaTableFH[j][i];\n\n            c /= mMaxTheta;\n\n            image[i][2] = ToU8(c.x);\n            image[i][1] = ToU8(c.y);\n            image[i][0] = ToU8(c.z);\n            image[i][3] = ToU8(mBRDFThetaTableH[j][i]);\n        }\n\n        image += width;\n    }\n}\n\nvoid SkyBRDF::FillBRDFTexture(int width, int height, float image[][4]) const\n{\n    VL_ASSERT(width == kTableSize);\n    VL_ASSERT((height == 2 * kBRDFSamples) || (mHasHTerm && height >= 3 * kBRDFSamples));\n\n    for (int j = 0; j < kBRDFSamples; j++)\n    {\n        for (int i = 0; i < width; i++)\n            *(Vec4f*) image[i] = Vec4f(mBRDFThetaTable[j][i], mHasHTerm ? mBRDFThetaTableH[j][i] : 1.0f);\n\n        image += width;\n    }\n\n    for (int j = 0; j < kBRDFSamples; j++)\n    {\n        for (int i = 0; i < width; i++)\n            *(Vec4f*) image[i] = Vec4f(mBRDFGammaTable[j][i], 1.0f);\n\n        image += width;\n    }\n\n    if (height < 3 * kBRDFSamples)\n        return;\n\n    // Want the H term too.\n    for (int j = 0; j < kBRDFSamples; j++)\n    {\n        for (int i = 0; i < width; i++)\n            *(Vec4f*) image[i] = Vec4f(mBRDFThetaTableFH[j][i], mBRDFThetaTableH[j][i]);\n\n        image += width;\n    }\n}\n\n\n//------------------------------------------------------------------------------\n// SunSky -- composite class for easier comparison\n//------------------------------------------------------------------------------\n\nSunSky::SunSky() :\n    mSkyType(kPreetham),\n    mToSun(vl_0),\n    mTurbidity(2.5f),\n    mAlbedo(vl_0),\n    mOvercast(0.0f),\n    mRoughness(0.0f),\n    mZenithY(0.0f)\n{\n}\n\nvoid SunSky::SetSkyType(tSkyType skyType)\n{\n    mSkyType = skyType;\n}\n\ntSkyType SunSky::SkyType() const\n{\n    return mSkyType;\n}\n\nvoid SunSky::SetSunDir(const Vec3f& v)\n{\n    mToSun = v;\n}\n\nvoid SunSky::SetTurbidity(float turbidity)\n{\n    mTurbidity = turbidity;\n}\n\nvoid SunSky::SetAlbedo(Vec3f rgb)\n{\n    mAlbedo = rgb;\n}\n\nvoid SunSky::SetOvercast(float overcast)\n{\n    mOvercast = overcast;\n}\n\nvoid SunSky::SetRoughness(float roughness)\n{\n    mRoughness = roughness;\n}\n\nvoid SunSky::Update()\n{\n    mZenithY = ZenithLuminance(acosf(mToSun.z), mTurbidity);\n\n    mPreetham.Update(mToSun, mTurbidity, mOvercast);\n\n    mHosek.mUseCubic = (kHosekCubic <= mSkyType && mSkyType <= kHosekCubicBRDF);\n    mHosek.Update(mToSun, mTurbidity, mAlbedo, mOvercast);\n\n    if (kPreethamTable <= mSkyType && mSkyType <= kPreethamBRDF)\n        mTable.FindThetaGammaTables(mPreetham);\n    else if (kHosekTable <= mSkyType && mSkyType <= kHosekBRDF)\n        mTable.FindThetaGammaTables(mHosek);\n    else if (kHosekCubicTable <= mSkyType && mSkyType <= kHosekCubicBRDF)\n        mTable.FindThetaGammaTables(mHosek);\n\n    if (mSkyType == kPreethamBRDF)\n        mBRDF.FindBRDFTables(mTable, mPreetham);\n    if (mSkyType == kHosekBRDF || mSkyType == kHosekCubicBRDF)\n        mBRDF.FindBRDFTables(mTable, mHosek);\n}\n\nVec3f SunSky::SkyRGB(const Vec3f& v) const\n{\n    switch (mSkyType)\n    {\n    case kPreetham:\n        return mPreetham.SkyRGB(v);\n\n    case kPreethamTable:\n        return mTable.SkyRGB(mPreetham, v);\n    case kPreethamBRDF:\n        return mBRDF.ConvolvedSkyRGB(mPreetham, v, mRoughness);\n\n    case kHosek:\n    case kHosekCubic:\n        return mHosek.SkyRGB(v);\n    case kHosekTable:\n    case kHosekCubicTable:\n        return mTable.SkyRGB(mHosek, v);\n    case kHosekBRDF:\n    case kHosekCubicBRDF:\n        return mBRDF.ConvolvedSkyRGB(mHosek, v, mRoughness);\n\n    case kCIEClear:\n        return Vec3f(CIEClearSkyLuminance       (v, mToSun, mZenithY));\n    case kCIEOvercast:\n        return Vec3f(CIEOvercastSkyLuminance    (v,         mZenithY));\n    case kCIEPartlyCloudy:\n        return Vec3f(CIEPartlyCloudySkyLuminance(v, mToSun, mZenithY));\n\n    default:\n        return vl_0;\n    }\n}\n\nfloat SunSky::SkyLuminance(const Vec3f& v) const\n{\n    if (v.z < 0.0)\n        return 0.0;\n\n    switch (mSkyType)\n    {\n    case kPreetham:\n        return mPreetham.SkyLuminance(v);\n    case kCIEClear:\n        return CIEClearSkyLuminance       (v, mToSun, mZenithY);\n    case kCIEOvercast:\n        return CIEOvercastSkyLuminance    (v,         mZenithY);\n    case kCIEPartlyCloudy:\n        return CIEPartlyCloudySkyLuminance(v, mToSun, mZenithY);\n    case kHosek:\n    case kHosekCubic:\n        return mHosek.SkyLuminance(v);\n    default:\n        return 0;\n    }\n}\n\nVec2f SunSky::SkyChroma(const Vec3f& v) const\n{\n    if (v.z < 0.0)\n        return Vec2f(0, 0);\n\n    switch (mSkyType)\n    {\n    case kPreetham:\n        return mPreetham.SkyChroma(v);\n    case kCIEClear:\n        return kClearChroma;\n    case kCIEOvercast:\n        return kOvercastChroma;\n    case kCIEPartlyCloudy:\n        return kPartlyCloudyChroma;\n    case kHosek:\n    case kHosekCubic:\n        return XYZToChroma(mHosek.SkyXYZ(v));\n    default:\n        return kOvercastChroma;\n    }\n}\n\nfloat SunSky::SkyAverageLuminance() const\n{\n    switch (mSkyType)\n    {\n    case kPreetham:\n    case kPreethamTable:\n    case kPreethamBRDF:\n        return mPreetham.mPerezInvDen.z;\n\n    case kCIEClear:\n        return CIEClearSkyLuminance(vl_0, mToSun, mZenithY);\n    case kCIEOvercast:\n        return mZenithY;\n    case kCIEPartlyCloudy:\n        return CIEPartlyCloudySkyLuminance(Vec3f(0.0f, 0.0f, 0.0f), mToSun, mZenithY);\n\n    case kHosek:\n    case kHosekTable:\n    case kHosekBRDF:\n    case kHosekCubic:\n    case kHosekCubicTable:\n    case kHosekCubicBRDF:\n        return mHosek.mRadXYZ.y;\n\n    default:\n        return 1.0f;\n    }\n}\n\nVec3f SunSky::SunRGB() const\n{\n    return ::SunRGB(mToSun.z, mTurbidity);\n}\n\nfloat SunSky::SunLuminance() const\n{\n    return ::SunLuminance(mToSun.z, mTurbidity);\n}\n\nVec2f SunSky::SunChroma() const\n{\n    return ::SunChroma(mToSun.z, mTurbidity);\n}\n\nVec3f SunSky::SunSkyRGB(const Vec3f& v, float overcast) const\n{\n    Vec3f result = SkyRGB(v);\n\n    if (dot(v, mToSun) >= kSunCosAngle && overcast < 1.0f)\n    {\n        float sunWeight = 1.0f - sqr(overcast);\n        result += ::SunRGB(mToSun.z, mTurbidity) * sunWeight;\n    }\n\n    return result;\n}\n\n"
  },
  {
    "path": "SunSky.hpp",
    "content": "//\n// SunSky.hpp\n//\n// Implements various sky models -- Preetham, Hosek, CIE* -- as\n// well as a separable table-based model for fast BRDF convolutions\n//\n// Andrew Willmott but:\n//   Preetham sun/sky based on code by Brian Smits,\n//   Hosek code used L. Hosek & A. Wilkie code Version 1.4a as reference\n//\n\n#ifndef SUN_SKY_H\n#define SUN_SKY_H\n\n#include \"VL234f.hpp\"\n\nnamespace SSLib\n{\n    extern const float kSunDiameter;\n    extern const float kSunDistance;\n    extern const float kSunCosAngle;\n    extern const float kSunSolidAngle;\n\n    Vec3f SunDirection\n    (\n        float       timeOfDay,         // 24-hour, decimal: 0.0-23.99\n        float       timeZone,          // relative to UTC: west -ve, east +ve\n        int         julianDay,         // day of year: 1-365\n        float       latitude,          // Degrees: north is positive\n        float       longitude          // Degrees: east is positive\n    );\n    // Returns the local sun direction at the given time/location. +Y = north, +X = east, +Z = up.\n\n    Vec2f SunriseAndSunset(float timeZone, int julianDay, float latitude, float longitude);\n    // Returns sunrise and sunset times for the given day and location.\n\n    // Utilities\n    Vec3f SunRGB      (float cosTheta, float turbidity = 3.0f);  // Returns RGB for given sun elevation\n    Vec2f SunChroma   (float cosTheta, float turbidity = 3.0f);  // Returns XYZ chroma for given sun elevation\n    float SunLuminance(float cosTheta, float turbidity = 3.0f);  // Returns XYZ luminance for given sun elevation\n\n    float ZenithLuminance(float thetaS, float T);           // Returns luminance estimate for given solar altitude and turbidity\n\n    float CIEOvercastSkyLuminance    (const Vec3f& v, float Lz);                        // CIE standard overcast sky\n    float CIEClearSkyLuminance       (const Vec3f& v, const Vec3f& toSun, float Lz);    // CIE standard clear sky\n    float CIEPartlyCloudySkyLuminance(const Vec3f& v, const Vec3f& toSun, float Lz);    // CIE standard partly cloudy sky\n    float CIEStandardSky   (int type, const Vec3f& v, const Vec3f& toSun, float Lz);    // Returns one of 15 standard skies: type = 0-14. See kCIEStandardSkyCoeffs\n\n\n    //--------------------------------------------------------------------------\n    // SkyPreetham\n    //--------------------------------------------------------------------------\n\n    class SkyPreetham\n    {\n    public:\n        SkyPreetham();\n\n        void        Update(const Vec3f& sun, float turbidity, float overcast = 0.0f, float hCrush = 0.0f); // update model with given settings\n\n        Vec3f       SkyRGB      (const Vec3f& v) const;     // Returns luminance/chroma converted to RGB\n        float       SkyLuminance(const Vec3f& v) const;     // Returns the luminance of the sky in direction v. v must be normalized. Luminance is in Nits = cd/m^2 = lumens/sr/m^2 */\n        Vec2f       SkyChroma   (const Vec3f& v) const;     // Returns the chroma of the sky in direction v. v must be normalized.\n\n        // Data\n        Vec3f       mToSun;\n\n        float       mPerez_x[5];\n        float       mPerez_y[5];\n        float       mPerez_Y[5];\n\n        Vec3f       mZenith;\n        Vec3f       mPerezInvDen;\n    };\n\n\n    //--------------------------------------------------------------------------\n    // SkyHosek\n    //--------------------------------------------------------------------------\n\n    class SkyHosek\n    {\n    public:\n        SkyHosek();\n\n        void        Update(const Vec3f& sun, float turbidity, Vec3f albedo = vl_0, float overcast = 0.0f); // update model with given settings\n\n        Vec3f       SkyXYZ      (const Vec3f& v) const;     // Returns CIE XYZ\n        Vec3f       SkyRGB      (const Vec3f& v) const;     // Returns luminance/chroma converted to RGB\n        float       SkyLuminance(const Vec3f& v) const;     // Returns CIE luminance\n\n        // Data\n        Vec3f       mToSun;\n        float       mCoeffsXYZ[3][9];   // Hosek 9-term distribution coefficients\n        Vec3f       mRadXYZ;            // Overall average radiance\n        Vec3f       mAlbedo;            // Ground albedo\n        bool        mUseCubic = false;  // Whether to use approximated cut-down Hosek\n    };\n\n\n    //--------------------------------------------------------------------------\n    // SkyTable\n    //--------------------------------------------------------------------------\n\n    class SkyTable\n    {\n    public:\n        // Table-based version - faster than per-sample Perez/Hosek function evaluation, suitable for shader use via 64 x 2 texture\n        // For a fixed time, Preetham can be expressed in the form\n        //   K (1 + F(theta))(1 + G(gamma))\n        // where theta is the zenith angle of v, and gamma the angle between v and the sun direction.\n        // Hosek can be expressed in the form\n        //   K (1 + F(theta))(1 + G(gamma) + H(theta))\n        // where H is trivial to evaluate in a shader, involving a constant term and sqrt(v.z).\n        // Note: the F term is generally negative, so we use F' = -F in the tables.\n\n        void        FindThetaGammaTables(const SkyPreetham& pt);\n        void        FindThetaGammaTables(const SkyHosek& pt);\n\n        Vec3f       SkyRGB(const SkyPreetham& pt, const Vec3f& v) const;  // Use precalculated table to return fast sky colour on CPU\n        Vec3f       SkyRGB(const SkyHosek& hk,    const Vec3f& v) const;  // Use precalculated table to return fast sky colour on CPU\n\n        void        FillTexture(int width, int height, uint8_t image[][4]) const;  // Fill kTableSize x 2 BGRA8 texture with tables\n        void        FillTexture(int width, int height, float   image[][4]) const;  // Fill kTableSize x 2 RGBAF32 texture with tables\n\n        // Table acceleration\n        enum { kTableSize = 64, kHalfTableSize = kTableSize / 2 };\n        Vec3f       mThetaTable[kTableSize];\n        Vec3f       mGammaTable[kTableSize];\n        float       mMaxTheta = 1.0f;       // To avoid clipping when using non-float textures. Currently only necessary if overcast is being used.\n        float       mMaxGamma = 1.0f;       // To avoid clipping when using non-float textures.\n        bool        mXYZ      = false;      // Whether tables are storing xyY (Preetham) or XYZ (Hosek)\n    };\n\n\n    //--------------------------------------------------------------------------\n    // SkyBRDF\n    //--------------------------------------------------------------------------\n\n    // #define COMPACT_BRDF_TABLE   // enable for smaller half-size table\n\n    class SkyBRDF\n    {\n    public:\n        // Extended version of SkyTable that uses zonal harmonics to produce table rows\n        // convolved with increasing cosine powers. This is an approximation, because\n        // conv(AB) != conv(A) conv(B), but, because the Perez-form evaluation is\n        // (1 + F(theta)) (1 + G(gamma)), the approximation is only for the small\n        // order-2 FG term.\n        void        FindBRDFTables(const SkyTable& table, const SkyPreetham& pt);\n        void        FindBRDFTables(const SkyTable& table, const SkyHosek& hk);\n\n        Vec3f       ConvolvedSkyRGB(const SkyPreetham& pt, const Vec3f& v, float roughness) const; // return sky term convolved with roughness, 1 = fully diffuse\n        Vec3f       ConvolvedSkyRGB(const SkyHosek& pt,    const Vec3f& v, float roughness) const; // return sky term convolved with roughness, 1 = fully diffuse\n\n        void        FillBRDFTexture(int width, int height, uint8_t image[][4]) const; // Fill kTableSize x (kBRDFSamples x 2|4) BGRA8 texture with tables\n        void        FillBRDFTexture(int width, int height, float   image[][4]) const; // Fill kTableSize x (kBRDFSamples x 2|4) RGBAF32 texture with tables\n                    // Note: for Hosek, the H term will be in the 'w' component of the theta section, and if a kBRDFSamples x 4 size texture is supplied,\n                    // the two additional sections will contain the FH term table. (Using this improves accuracy but can be skipped.)\n\n        enum { kTableSize = SkyTable::kTableSize, kHalfTableSize = SkyTable::kHalfTableSize };\n    #ifdef COMPACT_BRDF_TABLE\n        enum { kBRDFSamples = 4 };\n    #else\n        enum { kBRDFSamples = 8 };\n    #endif\n        Vec3f       mBRDFThetaTable[kBRDFSamples][kTableSize];\n        Vec3f       mBRDFGammaTable[kBRDFSamples][kTableSize];\n\n        // Additional tables for 'H' term in Hosek.\n        float       mBRDFThetaTableH [kBRDFSamples][kTableSize];\n        Vec3f       mBRDFThetaTableFH[kBRDFSamples][kTableSize];\n        bool        mHasHTerm = false;\n\n        float       mMaxTheta = 1.0f;       // To avoid clipping when using non-float textures. Currently only necessary if overcast is being used.\n        float       mMaxGamma = 1.0f;       // To avoid clipping when using non-float textures.\n        bool        mXYZ      = false;      // Whether tables are storing xyY (Preetham) or XYZ (Hosek)\n    };\n\n\n    //--------------------------------------------------------------------------\n    // SunSky\n    // Composite sun/sky model for easy comparisons\n    //--------------------------------------------------------------------------\n\n    enum tSkyType\n    {\n        kPreetham,\n        kPreethamTable, // Table accelerated version, for sanity checking -- really designed for texture/shader use\n        kPreethamBRDF,  // Sky model convolved with roughness (sat(cos^n) brdf)\n        kHosek,\n        kHosekTable,\n        kHosekBRDF,\n        kHosekCubic,\n        kHosekCubicTable,\n        kHosekCubicBRDF,\n        kCIEClear,\n        kCIEOvercast,\n        kCIEPartlyCloudy,\n        kNumSkyTypes\n    };\n\n    class SunSky\n    {\n    public:\n        SunSky();\n\n        void        SetSkyType(tSkyType skyType);\n        tSkyType    SkyType() const;\n\n        void        SetSunDir   (const Vec3f& sun);\n        void        SetTurbidity(float turbidity);\n        void        SetAlbedo   (Vec3f rgb);        // Set ground-bounce factor\n        void        SetOvercast (float overcast);   // 0 = clear, 1 = completely overcast\n        void        SetRoughness(float roughness);  // Set roughness for BRDF tables\n\n        void        Update();                       // update model given above settings\n\n        Vec3f       SkyRGB      (const Vec3f& v) const;  // Returns sky luminance/chroma converted to RGB\n        float       SkyLuminance(const Vec3f& v) const;  // Returns the luminance of the sky in direction v. v must be normalized. Luminance is in Nits = cd/m^2 = lumens/sr/m^2 */\n        Vec2f       SkyChroma   (const Vec3f& v) const;  // Returns the chroma of the sky in direction v. v must be normalized.\n        float       SkyAverageLuminance() const;         // Average over sky\n\n        Vec3f       SunRGB      () const;  // Returns direct sun RGB\n        float       SunLuminance() const;  // Returns direct sun's luminance\n        Vec2f       SunChroma   () const;  // Returns direct sun's chroma\n\n        Vec3f       SunSkyRGB(const Vec3f& v, float overcast = 0.0f) const;  // Returns combined sun + sky RGB. But generally you're better off treating these as separate light sources.\n\n    protected:\n        // Data\n        tSkyType    mSkyType;\n\n        Vec3f       mToSun;\n        float       mTurbidity;\n        Vec3f       mAlbedo;\n        float       mOvercast;\n        float       mRoughness;\n\n        // Various models\n        float       mZenithY;   // for CIE functions\n\n        SkyPreetham mPreetham;\n        SkyHosek    mHosek;\n        SkyTable    mTable;\n        SkyBRDF     mBRDF;\n    };\n}\n\n#endif\n"
  },
  {
    "path": "SunSkyTool.cpp",
    "content": "//\n//  SunSkyTool.cpp\n//\n//  Test tool for SunSky.*\n//\n//  Andrew Willmott\n//\n\n#define _CRT_SECURE_NO_WARNINGS\n#define _USE_MATH_DEFINES\n\n#include \"SunSky.hpp\"\n\n#include <math.h>\n#include <time.h>\n#include <stdio.h>\n#include <stdlib.h>\n#ifndef _MSC_VER\n    #include <unistd.h>\n    #include <strings.h>\n#else\n    #include <string.h>\n    #define strcasecmp _stricmp\n#endif\n\n#include \"stb_image_mini.h\"\n\n// #define EDGE_FILL\n\nusing namespace SSLib;\n\n//------------------------------------------------------------------------------\n// Utilities\n//------------------------------------------------------------------------------\n\nnamespace\n{\n    // latitude, longitude\n    const Vec2f kLondon       (+51.5f,  0.0f);\n    const Vec2f kAuckland     (-37.0f,  174.8f);\n    const Vec2f kPittsburgh   ( 40.5f, -80.22f);\n    const Vec2f kOakland      ( 37.8f, -122.2f);\n    const Vec2f kSanFrancisco ( 37.8f, -122.4f);\n    const Vec2f kJakarta      (-6.21f,  106.85f);\n\n    inline float saturate(float s)\n    {\n        if (s < 0.0f)\n            return 0.0f;\n        if (s >= 1.0f)\n            return 1.0f;\n        return s;\n    }\n\n    inline float Max(float a, float b)\n    {\n        return b < a ? a : b;\n    }\n\n    inline Vec3f MaxElts(const Vec3f& a, const Vec3f& b)\n    {\n        return Vec3f\n        (\n            Max(a.x, b.x),\n            Max(a.y, b.y),\n            Max(a.z, b.z)\n        );\n    }\n\n    inline uint32_t RGBFToU32(Vec3f rgb)\n    {\n        return\n              0xFF000000\n            | (int) lrintf(saturate(rgb.x) * 255.0f) <<  0\n            | (int) lrintf(saturate(rgb.y) * 255.0f) <<  8\n            | (int) lrintf(saturate(rgb.z) * 255.0f) << 16;\n    }\n\n    bool ArgCountError(const char* opt, int expected, int argc)\n    {\n        if (argc < expected)\n        {\n            fprintf(stderr, \"Not enough arguments for %s: expected %d, have %d\\n\", opt, expected, argc);\n            return true;\n        }\n\n        return false;\n    }\n\n    inline int HemiInset(float y2, int width)\n    {\n        float maxX2 = 1.0f - y2;\n        float maxX = sqrtf(maxX2);\n\n        return (int) lrintf(ceil((1.0f - maxX) * width / 2.0f));\n    }\n\n    Vec3f pow(Vec3f v, float n)\n    {\n        return Vec3f(powf(v.x, n), powf(v.y, n), powf(v.z, n));\n    }\n\n    Vec3f toneMapLinear(Vec3f c, float weight)\n    {\n        return c * weight;\n    }\n\n    Vec3f toneMapExp(Vec3f c, float weight)\n    {\n        return Vec3f(vl_one) - Vec3f(exp(-weight * c.x), exp(-weight * c.y), exp(-weight * c.z));\n    }\n\n    Vec3f toneMapReinhard(Vec3f c, float weight)\n    {\n        c *= weight;\n        return c / (Vec3f(vl_one) + c);\n    }\n\n    typedef Vec3f ToneMapFunc(Vec3f c, float weight);\n\n    enum kToneMapType\n    {\n        kToneMapLinear,\n        kToneMapExponential,\n        kToneMapReinhard,\n        kNumToneMapTypes\n    };\n\n    ToneMapFunc* kToneMapFuncs[kNumToneMapTypes + 1] =\n    {\n        toneMapLinear,\n        toneMapExp,\n        toneMapReinhard,\n        nullptr\n    };\n\n    struct MapInfo\n    {\n        float weight    = 5e-5f;\n        float gamma     = 2.2f;\n        float hemiSign  = 1.0f;\n        bool  fisheye   = false;\n\n        ToneMapFunc* toneMap = toneMapLinear;\n    };\n\n    bool PFMWrite(const char* filename, int width, int height, Vec3f* image)\n    {\n        FILE* f = fopen(filename, \"w\");\n\n        if (!f)\n            return false;\n\n        fprintf(f, \"PF\\n\");\n        fprintf(f, \"%d %d\\n\", width, height);\n        fprintf(f, \"-1.0\\n\");   // -ve = little endian\n\n        image += width * height;\n\n        for (int i = 0; i < height; i++)\n        {\n            image -= width;\n            fwrite(image, sizeof(float) * 3, width, f);\n        }\n\n        fclose(f);\n\n        return true;\n    }\n}\n\n\n//------------------------------------------------------------------------------\n// Projected (or fisheye) hemisphere in LDR (png) and HDR (pfm)\n//------------------------------------------------------------------------------\n\nnamespace\n{\n    /// Fill top-down projection of upper or lower hemisphere\n    void SkyToHemisphere(const SunSky& sunSky, int width, int height, uint8_t* data, int stride, const MapInfo& mi)\n    {\n        float invGamma = 1.0;\n\n        if (mi.gamma > 0.0)\n            invGamma = 1.0f / mi.gamma;\n\n        data += (height - 1) * stride;\n\n        for (int i = 0; i < height; i++)\n        {\n            uint32_t* row = (uint32_t*) data;\n\n            float y = 2.0f * (i + 0.5f) / height - 1.0f;\n            float y2 = y * y;\n\n            int sw = HemiInset(y2, width);\n\n            for (int j = sw; j < width - sw; j++)\n            {\n                float x = 2.0f * (j + 0.5f) / width - 1.0f;\n                float x2 = x * x;\n                float h2 = x2 + y2;\n\n                Vec3f v;\n\n                if (mi.fisheye)\n                {\n                    float theta = vl_halfPi - vl_halfPi * sqrtf(h2);\n                    float phi = atan2f(y, x);\n                    v = Vec3f(cos(phi) * cos(theta), sin(phi) * cos(theta), sin(theta));\n                }\n                else\n                    v = Vec3f(x, y, mi.hemiSign * sqrtf(1.0f - h2));\n\n                Vec3f c = sunSky.SkyRGB(v);\n\n                c = mi.toneMap(c, mi.weight);\n                c = pow(c, invGamma);\n\n                row[j] = RGBFToU32(c);\n            }\n\n            // fill in surrounds\n        #ifdef EDGE_FILL\n            for (int j = 0; j < sw; j++)\n                row[j] = row[sw];\n            for (int j = width - sw; j < width; j++)\n                row[j] = row[width - sw - 1];\n        #else\n            for (int j = 0; j < sw; j++)\n                row[j] = 0xFF000000;\n            for (int j = width - sw; j < width; j++)\n                row[j] = 0xFF000000;\n        #endif\n\n            data -= stride;\n        }\n    }\n\n    struct cStats\n    {\n        Vec3f avg;\n        Vec3f max;\n        Vec3f dev;\n    };\n\n    void SkyToHemisphere(const SunSky& sunSky, int width, int height, Vec3f* data, const MapInfo& mi, cStats* stats)\n    {\n        Vec3f maxElts(vl_0);\n        Vec3f sumElts(vl_0);\n        Vec3f varElts(vl_0);\n        int samples = 0;\n        int stride = width;\n\n        data += (height - 1) * stride;\n\n        for (int i = 0; i < height; i++)\n        {\n            Vec3f* row = data;\n\n            float y = 2.0f * (i + 0.5f) / height - 1.0f;\n            float y2 = y * y;\n\n            int sw = HemiInset(y2, width);\n\n            for (int j = sw; j < width - sw; j++)\n            {\n                float x = 2.0f * (j + 0.5f) / width - 1.0f;\n                float x2 = x * x;\n                float h2 = x2 + y2;\n\n                Vec3f v(x, y, mi.hemiSign * sqrtf(1.0f - h2));\n\n                Vec3f c = sunSky.SkyRGB(v);\n\n                if (stats)\n                {\n                    maxElts = MaxElts(maxElts, c);\n                    sumElts += c;\n                    varElts += c * c;\n                    samples++;\n                }\n\n                c *= mi.weight;\n\n                row[j] = c;\n            }\n\n        #ifdef EDGE_FILL\n            // fill in surrounds by replicating edge texels\n            for (int j = 0; j < sw; j++)\n                row[j] = row[sw];\n            for (int j = width - sw; j < width; j++)\n                row[j] = row[width - sw - 1];\n        #else\n            // fill in surrounds by replicating edge texels\n            for (int j = 0; j < sw; j++)\n                row[j] = vl_0;\n            for (int j = width - sw; j < width; j++)\n                row[j] = vl_0;\n        #endif\n\n            data -= stride;\n        }\n\n        if (stats)\n        {\n            stats->avg = sumElts / float(samples);\n            stats->max = maxElts;\n            varElts = varElts / float(samples) - sqr(stats->avg);\n            stats->dev = Vec3f(sqrtf(varElts.x), sqrtf(varElts.y), sqrtf(varElts.z));\n        }\n    }\n}\n\n\n//------------------------------------------------------------------------------\n// Cubemap generation in LDR (png) and HDR (pfm)\n//------------------------------------------------------------------------------\n\nnamespace\n{\n    const int kFaceIndices[6][3] =\n    {\n        { 0, 2, 1 },\n        { 2, 0, 1 },\n        { 0, 2, 1 },\n        { 2, 0, 1 },\n        { 0, 1, 2 },\n        { 0, 1, 2 },\n    };\n    const float kFaceSigns[6][3] =\n    {\n        { +1.0f, +1.0f, +1.0f },\n        { +1.0f, -1.0f, +1.0f },\n        { -1.0f, -1.0f, +1.0f },\n        { -1.0f, +1.0f, +1.0f },\n        { +1.0f, -1.0f, +1.0f },\n        { +1.0f, +1.0f, -1.0f },\n    };\n\n    void SkyToCubeFace(const SunSky& sunSky, int face, int width, int height, uint8_t* data, int stride, const MapInfo& mi)\n    {\n        float invGamma = 1.0;\n\n        if (mi.gamma > 0.0)\n            invGamma = 1.0f / mi.gamma;\n\n        const float* signs   = kFaceSigns  [face];\n        const int*   indices = kFaceIndices[face];\n\n        data += (height - 1) * stride;\n\n        for (int i = 0; i < height; i++)\n        {\n            uint32_t* row = (uint32_t*) data;\n\n            for (int j = 0; j < width; j++)\n            {\n                Vec3f facePos(2 * (j + 0.5f) / width - 1, 2 * (i + 0.5f) / height - 1, 1.0f);\n\n                Vec3f faceDir\n                (\n                    signs[0] * facePos[indices[0]],\n                    signs[1] * facePos[indices[1]],\n                    signs[2] * facePos[indices[2]]\n                );\n\n                faceDir = norm(faceDir);\n                Vec3f faceColour = sunSky.SkyRGB(faceDir);\n\n                faceColour = mi.toneMap(faceColour, mi.weight);\n                faceColour = pow(faceColour, invGamma);\n\n                row[j] = RGBFToU32(faceColour);\n            }\n\n            data -= stride;\n        }\n    }\n\n    void SkyToCubeFace(const SunSky& sunSky, int face, int width, int height, Vec3f* data, const MapInfo& mi)\n    {\n        const float* signs   = kFaceSigns  [face];\n        const int*   indices = kFaceIndices[face];\n\n        int stride = width;\n        data += (height - 1) * stride;\n\n        for (int i = 0; i < height; i++)\n        {\n            Vec3f* row = data;\n\n            for (int j = 0; j < width; j++)\n            {\n                Vec3f facePos(2 * (j + 0.5f) / width - 1, 2 * (i + 0.5f) / height - 1, 1.0f);\n\n                Vec3f faceDir\n                (\n                    signs[0] * facePos[indices[0]],\n                    signs[1] * facePos[indices[1]],\n                    signs[2] * facePos[indices[2]]\n                );\n\n                faceDir = norm(faceDir);\n\n                row[j] = sunSky.SkyRGB(faceDir) * mi.weight;\n            }\n\n            data -= stride;\n        }\n    }\n}\n\n\n//------------------------------------------------------------------------------\n// Panorama generation in LDR (png) and HDR (pfm)\n//------------------------------------------------------------------------------\n\nnamespace\n{\n    void SkyToPanoramic(const SunSky& sunSky, int height, uint8_t* data, int stride, const MapInfo& mi)\n    {\n        float invGamma = 1.0;\n\n        if (mi.gamma > 0.0)\n            invGamma = 1.0f / mi.gamma;\n\n        int width = 2 * height;\n        if (stride == 0)\n            stride = 4 * width;\n\n        float da = vl_pi / height;\n        float phi = vl_pi - 0.5f * da;\n\n        data += (height - 1) * stride;\n\n        for (int i = 0; i < height; i++)\n        {\n            uint32_t* row = (uint32_t*) data;\n\n            float theta = 0.5f * da;\n            float sp = sinf(phi);\n            float cp = cosf(phi);\n\n            for (int j = 0; j < width; j++)\n            {\n                float st = sinf(theta);\n                float ct = cosf(theta);\n\n                // middle of image is north, east to right, west to left, edges are south\n                Vec3f dir(-st * sp, -ct * sp, cp);\n\n                Vec3f c = sunSky.SkyRGB(dir);\n\n                c = mi.toneMap(c, mi.weight);\n                c = pow(c, invGamma);\n\n                row[j] = RGBFToU32(c);\n                theta += da;\n            }\n\n            data -= stride;\n            phi -= da;\n        }\n    }\n\n    void SkyToPanoramic(const SunSky& sunSky, int height, Vec3f* data, const MapInfo& mi)\n    {\n        int width = 2 * height;\n        int stride = width;\n\n        float da = vl_pi / height;\n        float phi = vl_pi - 0.5f * da;\n\n        data += (height - 1) * stride;\n\n        for (int i = 0; i < height; i++)\n        {\n            Vec3f* row = data;\n\n            float theta = 0.5f * da;\n            float sp = sinf(phi);\n            float cp = cosf(phi);\n\n            for (int j = 0; j < width; j++)\n            {\n                float st = sinf(theta);\n                float ct = cosf(theta);\n\n                // middle of image is north, east to right, west to left, edges are south\n                Vec3f dir(-st * sp, -ct * sp, cp);\n\n                row[j] = sunSky.SkyRGB(dir) * mi.weight;\n                theta += da;\n            }\n\n            data -= stride;\n            phi -= da;\n        }\n    }\n\n    const float kMinAutoLum    = 2000.0f;\n    const float kAutoLumTarget = 0.4f;\n}\n\n\n\n//------------------------------------------------------------------------------\n// Main program\n//------------------------------------------------------------------------------\n\nnamespace\n{\n    struct EnumInfo\n    {\n        const char* mName;\n        const char* mShort;\n        int         mValue;\n    };\n\n    EnumInfo kSkyTypeEnum[] =\n    {\n        { \"Preetham\",         \"pt\",   kPreetham        },\n        { \"PreethamTable\",    \"ptt\",  kPreethamTable   },\n        { \"PreethamBRDF\",     \"ptb\",  kPreethamBRDF    },\n        { \"Hosek\",            \"hk\",   kHosek           },\n        { \"HosekTable\",       \"hkt\",  kHosekTable      },\n        { \"HosekBRDF\",        \"hkb\",  kHosekBRDF       },\n        { \"HosekCubic\",       \"hc\",   kHosekCubic      },\n        { \"HosekCubicTable\",  \"hct\",  kHosekCubicTable },\n        { \"HosekCubicBRDF\",   \"hcb\",  kHosekCubicBRDF  },\n        { \"cieClear\",         \"cc\",   kCIEClear        },\n        { \"cieOvercast\",      \"co\",   kCIEOvercast     },\n        { \"ciePartlyCloudy\",  \"cp\",   kCIEPartlyCloudy },\n        { nullptr, nullptr, 0 }\n    };\n\n    EnumInfo kToneMapTypeEnum[] =\n    {\n        { \"linear\",       \"l\",   kToneMapLinear      },\n        { \"exponential\",  \"ex\",  kToneMapExponential },\n        { \"reinhard\",     \"rh\",  kToneMapReinhard    },\n        { nullptr, nullptr, 0 }\n    };\n\n    int ArgEnum(const EnumInfo info[], const char* name, int defaultValue = -1)\n    {\n        for ( ; info->mName; info++)\n            if (strcasecmp(info->mName, name) == 0 || strcasecmp(info->mShort, name) == 0)\n            {\n                return info->mValue;\n                break;\n            }\n\n        return defaultValue;\n    }\n\n    int Help(const char* command)\n    {\n        printf\n        (\n            \"%s <options>\\n\"\n            \"\\n\"\n            \"Options:\\n\"\n            \"  -h : this help\\n\"\n            \"  -s <skyType> : use given sky type (default: Preetham.)\\n\"\n            \"  -t <time>          : 0 - 24\\n\"\n            \"  -d <day of year>   : 0 - 365\\n\"\n            \"  -b <turbidity>     : 2 - 12\\n\"\n            \"  -x <l>|<r g b>     : 0 - 1, specify ground albedo for Hosek\\n\"\n            \"  -r <roughness>     : 0 - 1, specify roughness for BRDF types\\n\"\n            \"  -l <latitude> <longitude>\\n\"\n            \"  -w <luminance scale>\\n\"\n            \"  -g <gamma>\\n\"\n            \"  -e <tonemapType> : use given tonemap operator (default: linear)\\n\"\n            \"  -a : autoscale intensity\\n\"\n            \"  -i : invert hemisphere\\n\"\n            \"  -f : fisheye rather than cos projection\\n\"\n            \"  -c : output cubemap instead\\n\"\n            \"  -p : output panorama instead\\n\"\n            \"  -m : output movie, record day as sky.mp4, requires ffmpeg\\n\"\n            \"  -v : verbose\\n\"\n            , command\n        );\n\n        printf(\"\\n\" \"skyType:\\n\");\n        for (const EnumInfo* info = kSkyTypeEnum; info->mName; info++)\n            printf(\"  %-16s (%s)\\n\", info->mName, info->mShort);\n\n        printf(\"\\n\" \"toneMapType:\\n\");\n        for (const EnumInfo* info = kToneMapTypeEnum; info->mName; info++)\n            printf(\"  %-16s (%s)\\n\", info->mName, info->mShort);\n\n        return 0;\n    }\n}\n\nint main(int argc, const char* argv[])\n{\n    const char* command = argv[0];\n    argv++; argc--; // chomp\n\n    if (argc == 0)\n        return Help(command);\n\n    // Set up defaults\n    time_t unixTime;\n    ::time(&unixTime);\n\n    struct tm* theTime = localtime(&unixTime);\n\n    int julianDay = theTime->tm_yday;\n\n    float localTime = theTime->tm_hour + theTime->tm_min / 60.0f + theTime->tm_sec / 3600.0f;\n    bool dst = (theTime->tm_isdst != 0);\n    Vec2f latLong   = kLondon;\n    float turbidity = 2.5;\n    Vec3f albedo    = Vec3f(0.3f);\n    float overcast  = 0.0f;\n\n    MapInfo mi;\n    mi.weight = -1.0f;\n\n    float roughness = -1.0f;\n    bool autoscale  = false;\n    bool cubeMap    = false;\n    bool panoramic  = false;\n    bool movie      = false;\n    bool verbose    = false;\n    tSkyType skyType = kPreetham;\n\n    // Options\n    while (argc > 0 && argv[0][0] == '-')\n    {\n        const char* option = argv[0] + 1;\n        argv++; argc--;\n\n        switch (option[0])\n        {\n        case 'h':\n        case '?':\n            return Help(command);\n\n        case 't':\n            if (ArgCountError(option, 1, argc))\n                return -1;\n            localTime = (float) atof(argv[0]);\n            argv++; argc--;\n            break;\n\n        case 'd':\n            if (ArgCountError(option, 1, argc))\n                return -1;\n            julianDay = atoi(argv[0]);\n            argv++; argc--;\n            break;\n\n        case 'b':\n            if (ArgCountError(option, 1, argc))\n                return -1;\n            turbidity = (float) atof(argv[0]);\n            argv++; argc--;\n            break;\n\n        case 'o':\n            overcast = (float) atof(argv[0]);\n            argv++; argc--;\n            break;\n\n        case 'x':\n            if (ArgCountError(option, 1, argc))\n                return -1;\n            albedo.x = (float) atof(argv[0]);\n            argv++; argc--;\n\n            if (argc >= 1 && argv[0][0] != '-')\n            {\n                albedo.y = (float) atof(argv[0]);\n                argv++; argc--;\n            }\n            else\n                albedo.y = albedo.x;\n\n            if (argc >= 1 && argv[0][0] != '-')\n            {\n                albedo.z = (float) atof(argv[0]);\n                argv++; argc--;\n            }\n            else\n                albedo.z = albedo.y;\n\n            break;\n\n        case 'l':\n            if (ArgCountError(option, 2, argc))\n                return -1;\n            latLong[0] = (float) atof(argv[0]);\n            argv++; argc--;\n            latLong[1] = (float) atof(argv[0]);\n            argv++; argc--;\n\n            dst = false;    // don't take dst from local time info\n            break;\n\n        case 'w':\n            if (ArgCountError(option, 1, argc))\n                return -1;\n            mi.weight = (float) atof(argv[0]);\n            argv++; argc--;\n\n            break;\n\n        case 'g':\n            if (ArgCountError(option, 1, argc))\n                return -1;\n            mi.gamma = (float) atof(argv[0]);\n            argv++; argc--;\n            break;\n\n        case 'a':\n            autoscale = !autoscale;\n            break;\n        case 'c':\n            cubeMap = !cubeMap;\n            break;\n        case 'p':\n            panoramic = !panoramic;\n            break;\n        case 'm':\n            movie = !movie;\n            break;\n        case 'i':\n            mi.hemiSign = -mi.hemiSign;\n            break;\n        case 'f':\n            mi.fisheye = !mi.fisheye;\n            break;\n        case 'v':\n            verbose = !verbose;\n            break;\n\n        case 's':\n            {\n                if (ArgCountError(option, 1, argc))\n                    return -1;\n\n                const char* typeName = argv[0];\n                argv++; argc--;\n\n                skyType = (tSkyType) ArgEnum(kSkyTypeEnum, typeName, kNumSkyTypes);\n\n                if (skyType == kNumSkyTypes)\n                {\n                    fprintf(stderr, \"Unknown sky type: %s\\n\", typeName);\n                    return -1;\n                }\n            }\n            break;\n\n        case 'e':\n            {\n                if (ArgCountError(option, 1, argc))\n                    return -1;\n\n                const char* typeName = argv[0];\n                argv++; argc--;\n\n                mi.toneMap = kToneMapFuncs[ArgEnum(kToneMapTypeEnum, typeName, kNumToneMapTypes)];\n\n                if (!mi.toneMap)\n                {\n                    fprintf(stderr, \"Unknown tone map type: %s\\n\", typeName);\n                    return -1;\n                }\n            }\n            break;\n\n        case 'r':\n            if (ArgCountError(option, 1, argc))\n                return -1;\n            roughness = saturate((float) atof(argv[0]));\n            argv++; argc--;\n            break;\n\n\n        default:\n            fprintf(stderr, \"Unrecognised option: %s\\n\", option);\n            return -1;\n        }\n    }\n\n    if (argc > 0)\n    {\n        fprintf(stderr, \"Unrecognised arguments starting with %s\\n\", argv[0]);\n        return -1;\n    }\n\n    float timeZone = rintf(latLong[1] / 15.0f);    // estimate for now\n\n    if (dst)\n        timeZone += 1.0;\n\n    Vec3f sunDir = SunDirection(localTime, timeZone, julianDay, latLong[0], latLong[1]);\n\n    SunSky sunSky;\n    sunSky.SetSkyType(skyType);\n\n    sunSky.SetSunDir(sunDir);\n    sunSky.SetTurbidity(turbidity);\n    sunSky.SetAlbedo(albedo);\n    sunSky.SetOvercast(overcast);\n\n    if (roughness >= 0.0f)\n        sunSky.SetRoughness(roughness);\n\n    sunSky.Update();\n\n    if (verbose)\n    {\n        printf(\"Time: %g, time zone: %g, day: %d, latitude: %g, longitude: %g, turbidity: %g, albedo: %g\\n\", localTime, timeZone, julianDay, latLong[0], latLong[1], turbidity, albedo.y);\n\n        float theta = asinf (sunDir.z);\n        float phi   = atan2f(sunDir.y, sunDir.x);\n\n        theta *= 180.0f / vl_pi;\n        phi   *= 180.0f / vl_pi;\n\n        phi   = 90.0f - phi;    // make relative to North rather than East, and clockwise.\n\n        if (phi < 0.0f)\n            phi += 360.0f;\n\n        printf(\"Sun elevation      : %g\\n\", theta);\n        printf(\"Sun compass heading: %g\\n\", phi  );\n    }\n\n    if (mi.weight < 0.0f)\n    {\n        if (kHosek <= skyType && skyType <= kHosekCubicBRDF)\n            mi.weight = 8e-5f;\n        else\n            mi.weight = 5e-5f;\n    }\n\n    if (!movie && autoscale)\n    {\n        float avgLum = sunSky.SkyAverageLuminance();\n\n        if (verbose)\n            printf(\"Average luminance: %g\\n\", avgLum);\n\n        // Once we get dark enough (sun below horizon), stop auto-scaling, so we don't snap to black\n        if (avgLum < kMinAutoLum)\n            avgLum = kMinAutoLum;\n\n        float lumScale = kAutoLumTarget / avgLum;\n\n        if (verbose)\n            printf(\"Autoscaling luminance by: %g\\n\", lumScale);\n\n        mi.weight = lumScale;\n    }\n\n    if (verbose)\n        printf(\"Ouput: weight = %g, gamma = %g\\n\", mi.weight, mi.gamma);\n\n    char fileName[32];\n\n    if (panoramic)\n    {\n        uint32_t image   [256][512];\n        Vec3f    imageHDR[256][512];\n\n        SkyToPanoramic(sunSky, 256, (uint8_t*) image, 0, mi);\n\n        snprintf(fileName, 32, \"sky-panoramic.png\");\n\n        if (stbi_write_png(fileName, 512, 256, 4, image[0], 0) != 0)\n            printf(\"wrote %s\\n\", fileName);\n        else\n            printf(\"failed to write %s\\n\", fileName);\n\n        SkyToPanoramic(sunSky, 256, imageHDR[0], mi);\n\n        snprintf(fileName, 32, \"sky-panoramic.pfm\");\n\n        if (PFMWrite(fileName, 512, 256, imageHDR[0]))\n            printf(\"wrote %s\\n\", fileName);\n        else\n            printf(\"failed to write %s\\n\", fileName);\n    }\n    else if (cubeMap)\n    {\n        uint32_t image   [256][256];\n        Vec3f    imageHDR[256][256];\n\n        for (int i = 0; i < 6; i++)\n        {\n            SkyToCubeFace(sunSky, i, 256, 256, (uint8_t*) image, 4 * 256, mi);\n\n            snprintf(fileName, 32, \"sky-cube-%d.png\", i);\n\n            if (stbi_write_png(fileName, 256, 256, 4, image[0], 0) != 0)\n                printf(\"wrote %s\\n\", fileName);\n            else\n                printf(\"failed to write %s\\n\", fileName);\n\n            SkyToCubeFace(sunSky, i, 256, 256, imageHDR[0], mi);\n\n            snprintf(fileName, 32, \"sky-cube-%d.pfm\", i);\n\n            if (PFMWrite(fileName, 256, 256, imageHDR[0]))\n                printf(\"wrote %s\\n\", fileName);\n            else\n                printf(\"failed to write %s\\n\", fileName);\n        }\n    }\n#ifndef _MSC_VER\n    else if (movie)\n    {\n        uint32_t image[256][256];\n\n        // crf = constant rate factor, 0 - 51, 0 is lossless, 51 worst\n        // -preset = veryfast/faster/fast/medium/slow/slower/veryslow\n        const char* cmd = \"ffmpeg -r 60 -f rawvideo -pix_fmt rgba -s 256x256 -i - -threads 0 -preset medium -y -pix_fmt yuv420p -crf 10 sky.mp4\";\n\n        // open pipe to ffmpeg's stdin in binary write mode\n        FILE* ffmpeg = popen(cmd, \"w\");\n\n        if (!ffmpeg)\n        {\n            perror(\"cmd\");\n            return -1;\n        }\n\n        for (float time = 6.0f; time <= 21.0f; time += 0.1f)\n        {\n            sunSky.SetSunDir(SunDirection(time, timeZone, julianDay, latLong[0], latLong[1]));\n            sunSky.Update();\n\n            if (autoscale)\n            {\n                float avgLum = sunSky.SkyAverageLuminance();\n\n                if (verbose)\n                    printf(\"Average luminance: %g\\n\", avgLum);\n\n                // Once we get dark enough (sun below horizon), stop auto-scaling, so we don't snap to black\n                if (avgLum < kMinAutoLum)\n                    avgLum = kMinAutoLum;\n\n                float lumScale = kAutoLumTarget / avgLum;\n\n                if (verbose)\n                    printf(\"Autoscaling luminance by: %g\\n\", lumScale);\n\n                mi.weight = lumScale;\n            }\n\n            SkyToHemisphere(sunSky, 256, 256, (uint8_t*) image, 1024, mi);\n\n            fwrite(image, sizeof(image), 1, ffmpeg);\n        }\n\n        if (pclose(ffmpeg) == 0)\n            printf(\"wrote sky.mp4\\n\");\n        else\n            printf(\"failed to write sky.mp4\\n\");\n    }\n#endif\n    else\n    {\n        uint32_t image   [256][256];\n        Vec3f    imageHDR[256][256];\n\n        SkyToHemisphere(sunSky, 256, 256, (uint8_t*) image, 1024, mi);\n\n        snprintf(fileName, 32, \"sky-hemi.png\");\n\n        if (stbi_write_png(fileName, 256, 256, 4, image[0], 0) != 0)\n            printf(\"wrote %s\\n\", fileName);\n        else\n            printf(\"failed to write %s\\n\", fileName);\n\n        cStats stats;\n        SkyToHemisphere(sunSky, 256, 256, imageHDR[0], mi, verbose ? &stats : nullptr);\n\n        snprintf(fileName, 32, \"sky-hemi.pfm\");\n\n        if (PFMWrite(fileName, 256, 256, imageHDR[0]))\n            printf(\"wrote %s\\n\", fileName);\n        else\n            printf(\"failed to write %s\\n\", fileName);\n\n        if (verbose)\n        {\n            printf(\"avg: %8.2f, %8.2f, %8.2f\\n\", stats.avg.x, stats.avg.y, stats.avg.z);\n            printf(\"max: %8.2f, %8.2f, %8.2f\\n\", stats.max.x, stats.max.y, stats.max.z);\n            printf(\"dev: %8.2f, %8.2f, %8.2f\\n\", stats.dev.x, stats.dev.y, stats.dev.z);\n        }\n    }\n\n    return 0;\n}\n"
  },
  {
    "path": "VL234f.hpp",
    "content": "//\n// VL234f.hpp\n//\n// Andrew Willmott\n//\n\n#ifndef VL234f_H\n#define VL234f_H\n\n#include <cmath>\n// Default definitions\n\n#ifdef VL_NS\n    #define VL_NS_BEGIN namespace VL_NS {\n    #define VL_NS_END   }\n#else\n    #define VL_NS_BEGIN\n    #define VL_NS_END\n#endif\n\n#define VL_REPEAT 0x7FFFFFFF\n\n#define VL_PREFIX_(PREFIX, NAME) PREFIX ## _ ## NAME\n#define VL_PREFIX(PREFIX, NAME) VL_PREFIX_(PREFIX, NAME)\n\n// Assertions\n\n#ifndef VL_ASSERT_FULL\n    #ifdef VL_DEBUG\n        #include <assert.h>\n        #define VL_ASSERT_FULL(M_TYPE, M_B, ...) assert(M_B)\n    #else\n        #define VL_ASSERT_FULL(M_TYPE, M_B, ...)\n    #endif\n    #define VL_EXPECT_FULL(M_TYPE, M_B, ...) (void)(M_B)\n#endif\n\n#define VL_ASSERT_MSG(M_B, ...) VL_ASSERT_FULL(\"Assert Error\", M_B, __VA_ARGS__)\n#define VL_EXPECT_MSG(M_B, ...) VL_EXPECT_FULL(\"Warning\", M_B, __VA_ARGS__)\n#define VL_INDEX_MSG(M_I, M_N, ...) VL_ASSERT_FULL(\"Index Error\", (unsigned int)(M_I) < (unsigned int)(M_N), __VA_ARGS__)\n#define VL_RANGE_MSG(M_I, M_MIN, M_MAX, ...) VL_ASSERT_FULL(\"Range Error\", ((M_MIN) <= (M_I) && (M_I) < (M_MAX)), __VA_ARGS__)\n#define VL_ERROR(...) VL_ASSERT_FULL(\"Error\", false, __VA_ARGS__)\n#define VL_WARNING(...) VL_EXPECT_FULL(\"Warning\", false, __VA_ARGS__)\n\n#define VL_ASSERT(M_B) VL_ASSERT_MSG(M_B, #M_B)\n#define VL_EXPECT(M_B) VL_EXPECT_MSG(M_B, #M_B)\n#define VL_INDEX(M_I, M_N) VL_INDEX_MSG(M_I, M_N, \"0 <= \" #M_I \" < \" #M_N)\n#define VL_RANGE(M_I, M_MIN, M_MAX) VL_RANGE_MSG(M_I, M_MIN, M_MAX, #M_MIN \" <= \" #M_I \" < \" #M_MAX)\n\n// Memory\n\n#ifndef VL_NEW\n    #define VL_NEW new\n    #define VL_DELETE delete\n#endif\n\n// Misc\n\n#ifndef VL_CXX_11\n    #define VL_CXX_11 __cplusplus >= 201103L\n#endif\n\n#ifndef VL_CONSTANTS_H\n#define VL_CONSTANTS_H\n\n// --- Mathematical constants -------------------------------------------------\n\nenum    VLDiag       { vl_I = 1, vl_minus_I = -1, vl_nI = -1 };\nenum    VLBlock      { vl_zero = 0, vl_one = 1, vl_minus_one = -1, vl_0 = 0, vl_1 = 1, vl_n1 = -1 };\nenum    VLAxis       { vl_x, vl_y, vl_z, vl_w };\nenum    VLMinusAxis  { vl_minus_x, vl_minus_y, vl_minus_z, vl_minus_w, vl_nx = 0, vl_ny, vl_nz, vl_nw };\n\ntypedef VLAxis      vl_axis;        // e.g., Vecf(10, vl_axis(4)), Vec3f(vl_axis(i))\ntypedef VLMinusAxis vl_minus_axis;  // e.g., Vecf(10, vl_minus_axis(4))\n\nconst double vl_pi           = 3.14159265358979323846264338327950288;  // prefer vlf_pi, vld_pi etc.\nconst double vl_halfPi       = vl_pi / 2.0;\nconst double vl_quarterPi    = vl_pi / 4.0;\nconst double vl_twoPi        = vl_pi * 2.0;\n\n#ifdef HUGE_VAL\n    const float  vlf_huge = HUGE_VALF;\n    const double vld_huge = HUGE_VAL;\n#else\n    const float  vlf_huge = 1e50f;\n    const double vld_huge = 1e500;\n#endif\n\nconst double vl_huge = vld_huge;\n\nconst float  vlf_eps = 1.19209290E-07F;\nconst double vld_eps = 2.2204460492503131e-016;\n\nstruct VLVecType { typedef void IsVec; typedef float Elt; };\nstruct VLMatType { typedef void IsMat; typedef float Elt; };\nstruct VLVolType { typedef void IsVol; typedef float Elt; };\n\n#define VL_PREFIX_(PREFIX, NAME) PREFIX ## _ ## NAME\n#define VL_PREFIX(PREFIX, NAME) VL_PREFIX_(PREFIX, NAME)\n#define VL_CS(NAME) VL_PREFIX(vlf, NAME)\n\n#endif\n\n#ifndef VL_NO_REAL\nconst float VL_CS(pi)        = float(3.14159265358979323846264338327950288);\nconst float VL_CS(halfPi)    = float(VL_CS(pi) / 2);\nconst float VL_CS(quarterPi) = float(VL_CS(pi) / 4);\nconst float VL_CS(twoPi)     = float(VL_CS(pi) * 2);\n#endif\n\n#ifndef VL_MATH_H\n#define VL_MATH_H\n\n\n\nusing std::abs;\nusing std::sqrt;\n\n// --- Inlines ----------------------------------------------------------------\n\n// additions to arithmetic functions\n\ninline float  len   (float  x) { return abs(x); }\ninline double len   (double x) { return abs(x); }\ninline int    len   (int    x) { return abs(x); }\n\ninline float  sqrlen(float  x) { return x * x; }\ninline double sqrlen(double x) { return x * x; }\ninline int    sqrlen(int    x) { return x * x; }\n\ntemplate<class T> inline T sqr (T x) { return x * x; }\ntemplate<class T> inline T cube(T x) { return x * x * x; }\n\ninline float  sign(float a ) { return a < 0.0f ? -1.0f : 1.0f; }\ninline double sign(double a) { return a < 0.0  ? -1.0  : 1.0 ; }\n\ninline float  lerp(float  a, float  b, float  s) { return (1.0f - s) * a + s * b; }\ninline double lerp(double a, double b, double s) { return (1.0  - s) * a + s * b; }\n\ntemplate<class T, class S> T lerp(T x, T y, S s);\n\ntemplate<class T> inline T vl_min(T a, T b);\ntemplate<class T> inline T vl_max(T a, T b);\ntemplate<class T> inline T vl_clamp(T x, T min, T max);\ntemplate<class T> inline T vl_clamp_lower(T x, T min);\ntemplate<class T> inline T vl_clamp_upper(T x, T max);\n\nvoid vl_sincos(double phi, double* sinv, double* cosv);\nvoid vl_sincos(float phi, float* sinv, float* cosv);\n\n\n\n\n//\n// Implementation\n//\n\n\n\ntemplate<class T, class S> inline T lerp(T x, T y, S s)\n{\n    return x + (y - x) * s;\n}\n\ntemplate<class T> inline T vl_min(T a, T b)\n{\n    return a < b ? a : b;\n}\n\ntemplate<class T> inline T vl_max(T a, T b)\n{\n    return a > b ? a : b;\n}\n\ntemplate<class T> inline T vl_clamp(T x, T min, T max)\n{\n    return x < min ? min : (x > max ? max : x);\n}\n\ntemplate<class T> inline T vl_clamp_lower(T x, T min)\n{\n    return x < min ? min : x;\n}\n\ntemplate<class T> inline T vl_clamp_upper(T x, T max)\n{\n    return x > max ? max : x;\n}\n\n#ifdef VL_SINCOSF\n    inline void vl_sincos(double phi, double* sinv, double* cosv) { VL_SINCOS (phi, sinv, cosv); }\n    inline void vl_sincos(float  phi, float*  sinv, float*  cosv) { VL_SINCOSF(phi, sinv, cosv); }\n#elif defined(VL_SINCOS)\n    #define vl_sincos VL_SINCOS\n#else\n    inline void vl_sincos(double phi, double* sinv, double* cosv)\n    {\n        *sinv = sin(phi);\n        *cosv = cos(phi);\n    }\n    inline void vl_sincos(float phi, float* sinv, float* cosv)\n    {\n        *sinv = sinf(phi);\n        *cosv = cosf(phi);\n    }\n#endif\n\n\n\n#endif\n\n#ifndef VLF_VEC2_H\n#define VLF_VEC2_H\n\n\n// --- Vec2 Class -------------------------------------------------------------\n\n\nclass Vec2f : public VLVecType\n{\npublic:\n    // Constructors\n    Vec2f();\n    Vec2f(float x, float y);                 // (x, y)\n    Vec2f(const Vec2f& v);                 // Copy constructor\n\n    Vec2f(VLBlock      b);                 // vl_0, vl_1, ...\n    Vec2f(VLAxis       a, float s = vl_1);  // vl_x, vl_y\n    Vec2f(VLMinusAxis  a, float s = vl_1);  // vl_minus_x, vl_minus_y\n\n    explicit Vec2f(float s);\n    explicit Vec2f(const float v[]);\n\n    template<class T, class U = typename T::IsVec> explicit Vec2f(const T& v);\n\n    // Accessor functions\n    int          Elts() const { return 2; };   // Element count\n\n    float&        operator [] (int i);          // Indexing by row\n    const float&  operator [] (int i) const;    // Indexing by row\n\n    float*        Ref();                        // Return pointer to data\n    const float*  Ref() const;                  // Return pointer to data\n\n    // Assignment operators\n    Vec2f&       operator =  (const Vec2f& a);\n    Vec2f&       operator =  (VLBlock k);\n    Vec2f&       operator =  (VLAxis k);\n    Vec2f&       operator =  (VLMinusAxis k);\n\n    template<class T, class U = typename T::IsVec> Vec2f& operator = (const T& v);\n\n    Vec2f&       operator += (const Vec2f& a);\n    Vec2f&       operator -= (const Vec2f& a);\n    Vec2f&       operator *= (const Vec2f& a);\n    Vec2f&       operator *= (float s);\n    Vec2f&       operator /= (const Vec2f& a);\n    Vec2f&       operator /= (float s);\n\n    // Comparison operators\n    bool         operator == (const Vec2f& a) const; // v == a?\n    bool         operator != (const Vec2f& a) const; // v != a?\n    bool         operator <  (const Vec2f& a) const; // All v.i <  a.i?\n    bool         operator >  (const Vec2f& a) const; // All v.i >  a.i?\n    bool         operator <= (const Vec2f& a) const; // All v.i <= a.i?\n    bool         operator >= (const Vec2f& a) const; // All v.i >= a.i?\n\n    // Arithmetic operators\n    Vec2f        operator + (const Vec2f& a) const;  // v + a\n    Vec2f        operator - (const Vec2f& a) const;  // v - a\n    const Vec2f& operator + () const;                // +v\n    Vec2f        operator - () const;                // -v\n    Vec2f        operator * (const Vec2f& a) const;  // v * a (vx * ax, ...)\n    Vec2f        operator * (float s) const;          // v * s\n    Vec2f        operator / (const Vec2f& a) const;  // v / a (vx / ax, ...)\n    Vec2f        operator / (float s) const;          // v / s\n\n    // Initialisers\n    Vec2f&       MakeZero();                         // Zero vector\n    Vec2f&       MakeUnit(int i, float k = vl_one);   // I[i]\n    Vec2f&       MakeBlock(float k = vl_one);         // All-k vector\n\n    // Data\n    float  x;\n    float  y;\n};\n\n\n// --- Vec2 operators ---------------------------------------------------------\n\nVec2f operator * (float s, const Vec2f& v);       // s * v\n\nfloat  dot      (const Vec2f& a, const Vec2f& b); // a . b\nVec2f cross    (const Vec2f& v);                 // ccw orthogonal vector to 'v'. cross(vl_x) = vl_y.\nfloat  sqrlen   (const Vec2f& v);                 // v . v\n#ifndef VL_NO_REAL\nfloat  len      (const Vec2f& v);                 // || v ||\nVec2f norm     (const Vec2f& v);                 // v / || v ||\nVec2f norm_safe(const Vec2f& v);                 // v / || v ||, handles || v || = 0\nVec2f inv      (const Vec2f& v);                 // inverse: 1 / v\n#endif\nVec2f abs      (const Vec2f& v);                 // abs(v_i)\n\n\n// --- Inlines ----------------------------------------------------------------\n\n\ninline Vec2f::Vec2f()\n{\n}\n\ninline Vec2f::Vec2f(float a, float b) :\n    x(a),\n    y(b)\n{}\n\ninline Vec2f::Vec2f(const Vec2f& v) :\n    x(v.x),\n    y(v.y)\n{}\n\ninline Vec2f::Vec2f(float s) :\n    x(s),\n    y(s)\n{\n}\n\ninline Vec2f::Vec2f(const float v[]) :\n    x(v[0]),\n    y(v[1])\n{\n}\n\ntemplate<class T, class U> inline Vec2f::Vec2f(const T& v)\n{\n    VL_ASSERT_MSG(Elts() == v.Elts() || v.Elts() == VL_REPEAT, \"(Vec2) Vector sizes don't match\");\n\n    for (int i = 0; i < v.Elts(); i++)\n        (*this)[i] = float(v[i]);\n}\n\ninline float& Vec2f::operator [] (int i)\n{\n    VL_RANGE_MSG(i, 0, 2, \"(Vec2::[i]) index out of range\");\n    return (&x)[i];\n}\n\ninline const float& Vec2f::operator [] (int i) const\n{\n    VL_RANGE_MSG(i, 0, 2, \"(Vec2::[i]) index out of range\");\n    return (&x)[i];\n}\n\ninline float* Vec2f::Ref()\n{\n    return &x;\n}\n\ninline const float* Vec2f::Ref() const\n{\n    return &x;\n}\n\ninline Vec2f& Vec2f::operator = (const Vec2f& v)\n{\n    x = v.x;\n    y = v.y;\n\n    return *this;\n}\n\ntemplate<class T, class U> inline Vec2f& Vec2f::operator = (const T& v)\n{\n    VL_ASSERT_MSG(Elts() == v.Elts() || v.Elts() == VL_REPEAT, \"(Vec2::=) Vector sizes don't match\");\n\n    for (int i = 0; i < Elts(); i++)\n        (*this)[i] = float(v[i]);\n\n    return *this;\n}\n\ninline Vec2f& Vec2f::operator += (const Vec2f& v)\n{\n    x += v.x;\n    y += v.y;\n\n    return *this;\n}\n\ninline Vec2f& Vec2f::operator -= (const Vec2f& v)\n{\n    x -= v.x;\n    y -= v.y;\n\n    return *this;\n}\n\ninline Vec2f& Vec2f::operator *= (const Vec2f& v)\n{\n    x *= v.x;\n    y *= v.y;\n\n    return *this;\n}\n\ninline Vec2f& Vec2f::operator *= (float s)\n{\n    x *= s;\n    y *= s;\n\n    return *this;\n}\n\ninline Vec2f& Vec2f::operator /= (const Vec2f& v)\n{\n    x /= v.x;\n    y /= v.y;\n\n    return *this;\n}\n\ninline Vec2f& Vec2f::operator /= (float s)\n{\n    x /= s;\n    y /= s;\n\n    return *this;\n}\n\ninline Vec2f Vec2f::operator + (const Vec2f& a) const\n{\n    Vec2f result;\n\n    result.x = x + a.x;\n    result.y = y + a.y;\n\n    return result;\n}\n\ninline Vec2f Vec2f::operator - (const Vec2f& a) const\n{\n    Vec2f result;\n\n    result.x = x - a.x;\n    result.y = y - a.y;\n\n    return result;\n}\n\ninline const Vec2f& Vec2f::operator + () const\n{\n    return *this;\n}\n\ninline Vec2f Vec2f::operator - () const\n{\n    Vec2f result;\n\n    result.x = -x;\n    result.y = -y;\n\n    return result;\n}\n\ninline Vec2f Vec2f::operator * (const Vec2f& a) const\n{\n    Vec2f result;\n\n    result.x = x * a.x;\n    result.y = y * a.y;\n\n    return result;\n}\n\ninline Vec2f Vec2f::operator * (float s) const\n{\n    Vec2f result;\n\n    result.x = x * s;\n    result.y = y * s;\n\n    return result;\n}\n\ninline Vec2f operator * (float s, const Vec2f& v)\n{\n    return v * s;\n}\n\ninline Vec2f Vec2f::operator / (const Vec2f& a) const\n{\n    Vec2f result;\n\n    result.x = x / a.x;\n    result.y = y / a.y;\n\n    return result;\n}\n\ninline Vec2f Vec2f::operator / (float s) const\n{\n    Vec2f result;\n\n    result.x = x / s;\n    result.y = y / s;\n\n    return result;\n}\n\ninline float dot(const Vec2f& a, const Vec2f& b)\n{\n    return a.x * b.x + a.y * b.y;\n}\n\ninline Vec2f cross(const Vec2f& a)\n{\n    Vec2f result;\n\n    result.x = -a.y;\n    result.y =  a.x;\n\n    return result;\n}\n\ninline float sqrlen(const Vec2f& v)\n{\n    return dot(v, v);\n}\n\n#ifndef VL_NO_REAL\ninline float len(const Vec2f& v)\n{\n    return sqrt(dot(v, v));\n}\n\ninline Vec2f norm(const Vec2f& v)\n{\n    VL_ASSERT_MSG(sqrlen(v) > float(vl_zero), \"normalising length-zero vector\");\n    return v / len(v);\n}\n\ninline Vec2f norm_safe(const Vec2f& v)\n{\n    return v / (len(v) + float(1e-8));\n}\n\ninline Vec2f inv(const Vec2f& v)\n{\n    return Vec2f(float(1) / v.x, float(1) / v.y);\n}\n#endif\n\ninline Vec2f abs(const Vec2f& v)\n{\n    return Vec2f(abs(v.x), abs(v.y));\n}\n\ninline Vec2f& Vec2f::MakeUnit(int i, float k)\n{\n    if (i == 0)\n    { x = k; y = vl_zero; }\n    else if (i == 1)\n    { x = vl_zero; y = k; }\n    else\n        VL_ERROR(\"(Vec2::Unit) illegal unit vector\");\n\n    return *this;\n}\n\ninline Vec2f& Vec2f::MakeZero()\n{\n    x = vl_zero; y = vl_zero;\n    return *this;\n}\n\ninline Vec2f& Vec2f::MakeBlock(float k)\n{\n    x = k; y = k;\n    return *this;\n}\n\n\ninline Vec2f::Vec2f(VLBlock k) :\n    x(float(k)),\n    y(float(k))\n{\n}\n\ninline Vec2f::Vec2f(VLAxis k, float s)\n{\n    MakeUnit(k, s);\n}\n\ninline Vec2f::Vec2f(VLMinusAxis k, float s)\n{\n    MakeUnit(k, -s);\n}\n\ninline Vec2f& Vec2f::operator = (VLBlock k)\n{\n    MakeBlock(float(k));\n    return *this;\n}\n\ninline Vec2f& Vec2f::operator = (VLAxis k)\n{\n    MakeUnit(k);\n    return *this;\n}\n\ninline Vec2f& Vec2f::operator = (VLMinusAxis k)\n{\n    MakeUnit(k, vl_minus_one);\n    return *this;\n}\n\ninline bool Vec2f::operator == (const Vec2f& a) const\n{\n    return x == a.x && y == a.y;\n}\n\ninline bool Vec2f::operator != (const Vec2f& a) const\n{\n    return x != a.x || y != a.y;\n}\n\ninline bool Vec2f::operator < (const Vec2f& a) const\n{\n    return x < a.x && y < a.y;\n}\n\ninline bool Vec2f::operator > (const Vec2f& a) const\n{\n    return x > a.x && y > a.y;\n}\n\ninline bool Vec2f::operator <= (const Vec2f& a) const\n{\n    return x <= a.x && y <= a.y;\n}\n\ninline bool Vec2f::operator >= (const Vec2f& a) const\n{\n    return x >= a.x && y >= a.y;\n}\n\n#endif\n\n#ifndef VLF_VEC3_H\n#define VLF_VEC3_H\n\n\n// --- Vec3 Class -------------------------------------------------------------\n\n\nclass Vec2f;\n\nclass Vec3f : public VLVecType\n{\npublic:\n    // Constructors\n    Vec3f();\n    Vec3f(float x, float y, float z);         // [x, y, z]\n    Vec3f(const Vec3f& v);                 // Copy constructor\n    Vec3f(const Vec2f& v, float w);         // Hom. 2D vector\n\n    Vec3f(VLBlock     b);                  // vl_0, vl_1, ...\n    Vec3f(VLAxis      a,  float s = vl_1);  // vl_x, vl_y\n    Vec3f(VLMinusAxis a,  float s = vl_1);  // vl_minus_x, vl_minus_y\n\n    explicit Vec3f(float s);\n    explicit Vec3f(const float v[]);\n\n    template<class T, class U = typename T::IsVec> explicit Vec3f(const T& v);\n\n    // Accessor functions\n    int          Elts() const { return 3; };   // Element count\n\n    float&        operator [] (int i);          // Indexing by row\n    const float&  operator [] (int i) const;    // Indexing by row\n\n    float*        Ref();                        // Return pointer to data\n    const float*  Ref() const;                  // Return pointer to data\n\n    // Assignment operators\n    Vec3f&       operator =  (const Vec3f& a);\n    Vec3f&       operator =  (VLBlock k);\n    Vec3f&       operator =  (VLAxis k);\n    Vec3f&       operator =  (VLMinusAxis k);\n\n    template<class T, class U = typename T::IsVec> Vec3f& operator = (const T& v);\n\n    Vec3f&       operator += (const Vec3f& a);\n    Vec3f&       operator -= (const Vec3f& a);\n    Vec3f&       operator *= (const Vec3f& a);\n    Vec3f&       operator *= (float s);\n    Vec3f&       operator /= (const Vec3f& a);\n    Vec3f&       operator /= (float s);\n\n    // Comparison operators\n    bool         operator == (const Vec3f& a) const; // v == a?\n    bool         operator != (const Vec3f& a) const; // v != a?\n    bool         operator <  (const Vec3f& a) const; // All v.i <  a.i?\n    bool         operator >= (const Vec3f& a) const; // All v.i >= a.i?\n\n    // Arithmetic operators\n    Vec3f        operator + (const Vec3f& a) const;  // v + a\n    Vec3f        operator - (const Vec3f& a) const;  // v - a\n    const Vec3f& operator + () const;                // -v\n    Vec3f        operator - () const;                // -v\n    Vec3f        operator * (const Vec3f& a) const;  // v * a (vx * ax, ...)\n    Vec3f        operator * (float s) const;          // v * s\n    Vec3f        operator / (const Vec3f& a) const;  // v / a (vx / ax, ...)\n    Vec3f        operator / (float s) const;          // v / s\n\n    // Initialisers\n    Vec3f&       MakeZero();                        // Zero vector\n    Vec3f&       MakeUnit(int i, float k = vl_one);  // I[i]\n    Vec3f&       MakeBlock(float k = vl_one);        // All-k vector\n\n    // Conversion\n    Vec2f&       AsVec2();\n    const Vec2f& AsVec2() const;\n\n    // Data\n    float x;\n    float y;\n    float z;\n};\n\n\n// --- Vec3 operators ---------------------------------------------------------\n\nVec3f operator * (float s, const Vec3f& v);       // s * v\n\nfloat  dot(const Vec3f& a, const Vec3f& b);       // a . b\nVec3f cross    (const Vec3f& a, const Vec3f& b); // a x b\nVec3f cross_x  (const Vec3f& v);                 // v x e_x\nVec3f cross_y  (const Vec3f& v);                 // v x e_y\nVec3f cross_z  (const Vec3f& v);                 // v x e_z\nfloat  sqrlen   (const Vec3f& v);                 // v . v\n#ifndef VL_NO_REAL\nfloat  len      (const Vec3f& v);                 // || v ||\nVec3f norm     (const Vec3f& v);                 // v / || v ||\nVec3f norm_safe(const Vec3f& v);                 // v / || v ||, handles || v || = 0\nVec3f inv      (const Vec3f& v);                 // inverse: 1 / v\nVec2f proj     (const Vec3f& v);                 // homogeneous projection\n#endif\nVec3f abs      (const Vec3f& v);                 // abs(v_i)\n\n\n// --- Inlines ----------------------------------------------------------------\n\n\ninline Vec3f::Vec3f()\n{\n}\n\ninline Vec3f::Vec3f(float a, float b, float c) :\n    x(a),\n    y(b),\n    z(c)\n{}\n\ninline Vec3f::Vec3f(const Vec3f& v)  :\n    x(v.x),\n    y(v.y),\n    z(v.z)\n{}\n\ninline Vec3f::Vec3f(const Vec2f& v, float z_in) :\n    x(v.x),\n    y(v.y),\n    z(z_in)\n{}\n\ninline Vec3f::Vec3f(float s) :\n    x(s),\n    y(s),\n    z(s)\n{}\n\ninline Vec3f::Vec3f(const float v[]) :\n    x(v[0]),\n    y(v[1]),\n    z(v[2])\n{}\n\ntemplate<class T, class U> inline Vec3f::Vec3f(const T& v)\n{\n    VL_ASSERT_MSG(Elts() == v.Elts() || v.Elts() == VL_REPEAT, \"(Vec3) Vector sizes don't match\");\n\n    for (int i = 0; i < v.Elts(); i++)\n        (*this)[i] = float(v[i]);\n}\n\ninline float& Vec3f::operator [] (int i)\n{\n    VL_RANGE_MSG(i, 0, 3, \"(Vec3::[i]) index out of range\");\n    return (&x)[i];\n}\n\ninline const float& Vec3f::operator [] (int i) const\n{\n    VL_RANGE_MSG(i, 0, 3, \"(Vec3::[i]) index out of range\");\n    return (&x)[i];\n}\n\ninline float* Vec3f::Ref()\n{\n    return &x;\n}\n\ninline const float* Vec3f::Ref() const\n{\n    return &x;\n}\n\ninline Vec3f& Vec3f::operator = (const Vec3f& v)\n{\n    x = v.x;\n    y = v.y;\n    z = v.z;\n\n    return *this;\n}\n\ntemplate<class T, class U> inline Vec3f& Vec3f::operator = (const T& v)\n{\n    VL_ASSERT_MSG(Elts() == v.Elts() || v.Elts() == VL_REPEAT, \"(Vec3::=) Vector sizes don't match\");\n\n    for (int i = 0; i < Elts(); i++)\n        (*this)[i] = float(v[i]);\n\n    return *this;\n}\n\ninline Vec3f& Vec3f::operator += (const Vec3f& v)\n{\n    x += v.x;\n    y += v.y;\n    z += v.z;\n\n    return *this;\n}\n\ninline Vec3f& Vec3f::operator -= (const Vec3f& v)\n{\n    x -= v.x;\n    y -= v.y;\n    z -= v.z;\n\n    return *this;\n}\n\ninline Vec3f& Vec3f::operator *= (const Vec3f& a)\n{\n    x *= a.x;\n    y *= a.y;\n    z *= a.z;\n\n    return *this;\n}\n\ninline Vec3f& Vec3f::operator *= (float s)\n{\n    x *= s;\n    y *= s;\n    z *= s;\n\n    return *this;\n}\n\ninline Vec3f& Vec3f::operator /= (const Vec3f& a)\n{\n    x /= a.x;\n    y /= a.y;\n    z /= a.z;\n\n    return *this;\n}\n\ninline Vec3f& Vec3f::operator /= (float s)\n{\n    x /= s;\n    y /= s;\n    z /= s;\n\n    return *this;\n}\n\ninline Vec3f Vec3f::operator + (const Vec3f& a) const\n{\n    Vec3f result;\n\n    result.x = x + a.x;\n    result.y = y + a.y;\n    result.z = z + a.z;\n\n    return result;\n}\n\ninline Vec3f Vec3f::operator - (const Vec3f& a) const\n{\n    Vec3f result;\n\n    result.x = x - a.x;\n    result.y = y - a.y;\n    result.z = z - a.z;\n\n    return result;\n}\n\ninline const Vec3f& Vec3f::operator + () const\n{\n    return *this;\n}\n\ninline Vec3f Vec3f::operator - () const\n{\n    Vec3f result;\n\n    result.x = -x;\n    result.y = -y;\n    result.z = -z;\n\n    return result;\n}\n\ninline Vec3f Vec3f::operator * (const Vec3f& a) const\n{\n    Vec3f result;\n\n    result.x = x * a.x;\n    result.y = y * a.y;\n    result.z = z * a.z;\n\n    return result;\n}\n\ninline Vec3f Vec3f::operator * (float s) const\n{\n    Vec3f result;\n\n    result.x = x * s;\n    result.y = y * s;\n    result.z = z * s;\n\n    return result;\n}\n\ninline Vec3f Vec3f::operator / (const Vec3f& a) const\n{\n    Vec3f result;\n\n    result.x = x / a.x;\n    result.y = y / a.y;\n    result.z = z / a.z;\n\n    return result;\n}\n\ninline Vec3f Vec3f::operator / (float s) const\n{\n    Vec3f result;\n\n    result.x = x / s;\n    result.y = y / s;\n    result.z = z / s;\n\n    return result;\n}\n\ninline Vec3f operator * (float s, const Vec3f& v)\n{\n    return v * s;\n}\n\ninline Vec3f& Vec3f::MakeUnit(int n, float k)\n{\n    switch (n)\n    {\n    case 0:\n        { x = k; y = vl_zero; z = vl_zero; } break;\n    case 1:\n        { x = vl_zero; y = k; z = vl_zero; } break;\n    case 2:\n        { x = vl_zero; y = vl_zero; z = k; } break;\n    default:\n        VL_ERROR(\"(Vec3::Unit) illegal unit vector\");\n    }\n\n    return *this;\n}\n\ninline Vec3f& Vec3f::MakeZero()\n{\n    x = vl_zero; y = vl_zero; z = vl_zero;\n    return *this;\n}\n\ninline Vec3f& Vec3f::MakeBlock(float k)\n{\n    x = k; y = k; z = k;\n    return *this;\n}\n\n\ninline Vec3f::Vec3f(VLBlock k) :\n    x(float(k)),\n    y(float(k)),\n    z(float(k))\n{\n}\n\ninline Vec3f::Vec3f(VLAxis a, float s)\n{\n    MakeUnit(a, s);\n}\n\ninline Vec3f::Vec3f(VLMinusAxis a, float s)\n{\n    MakeUnit(a, -s);\n}\n\ninline Vec3f& Vec3f::operator = (VLBlock k)\n{\n    MakeBlock(float(k));\n    return *this;\n}\n\ninline Vec3f& Vec3f::operator = (VLAxis k)\n{\n    MakeUnit(k);\n    return *this;\n}\n\ninline Vec3f& Vec3f::operator = (VLMinusAxis k)\n{\n    MakeUnit(k, vl_minus_one);\n    return *this;\n}\n\ninline bool Vec3f::operator == (const Vec3f& a) const\n{\n    return x == a.x && y == a.y && z == a.z;\n}\n\ninline bool Vec3f::operator != (const Vec3f& a) const\n{\n    return x != a.x || y != a.y || z != a.z;\n}\n\ninline bool Vec3f::operator < (const Vec3f& a) const\n{\n    return x < a.x && y < a.y && z < a.z;\n}\n\ninline bool Vec3f::operator >= (const Vec3f& a) const\n{\n    return x >= a.x && y >= a.y && z >= a.z;\n}\n\ninline Vec2f& Vec3f::AsVec2()\n{\n    return (Vec2f&) *this;\n}\n\ninline const Vec2f& Vec3f::AsVec2() const\n{\n    return (const Vec2f&) *this;\n}\n\n\ninline float dot(const Vec3f& a, const Vec3f& b)\n{\n    return a.x * b.x + a.y * b.y + a.z * b.z;\n}\n\ninline Vec3f cross(const Vec3f& a, const Vec3f& b)\n{\n    Vec3f result;\n\n    result.x = a.y * b.z - a.z * b.y;\n    result.y = a.z * b.x - a.x * b.z;\n    result.z = a.x * b.y - a.y * b.x;\n\n    return result;\n}\n\ninline Vec3f cross_x(const Vec3f& v)\n{ return Vec3f(float(vl_zero), v.z, -v.y); }\n\ninline Vec3f cross_y(const Vec3f& v)\n{ return Vec3f(-v.z, float(vl_zero), v.x); }\n\ninline Vec3f cross_z(const Vec3f& v)\n{ return Vec3f(v.y, -v.x, float(vl_zero)); }\n\ninline float sqrlen(const Vec3f& v)\n{\n    return dot(v, v);\n}\n\n#ifndef VL_NO_REAL\ninline float len(const Vec3f& v)\n{\n    return sqrt(float(dot(v, v)));\n}\n\ninline Vec3f norm(const Vec3f& v)\n{\n    VL_ASSERT_MSG(sqrlen(v) > float(vl_zero), \"normalising length-zero vector\");\n    return v / len(v);\n}\n\ninline Vec3f norm_safe(const Vec3f& v)\n{\n    return v / (len(v) + float(1e-8));\n}\n\ninline Vec3f inv(const Vec3f& v)\n{\n    return Vec3f(float(1) / v.x, float(1) / v.y, float(1) / v.z);\n}\n\ninline Vec2f proj(const Vec3f& v)\n{\n    Vec2f result;\n\n    VL_ASSERT_MSG(v.z != float(vl_zero), \"(Vec3/proj) last elt. is zero\");\n\n    result.x = v.x / v.z;\n    result.y = v.y / v.z;\n\n    return result;\n}\n#endif\n\ninline Vec3f abs(const Vec3f& v)\n{\n    return Vec3f(abs(v.x), abs(v.y), abs(v.z));\n}\n\n#endif\n\n#ifndef VLF_VEC4_H\n#define VLF_VEC4_H\n\n\n// --- Vec4 Class -------------------------------------------------------------\n\n\nclass Vec2f;\nclass Vec3f;\n\nclass Vec4f : public VLVecType\n{\npublic:\n    // Constructors\n    Vec4f();\n    Vec4f(float x, float y, float z, float w);      // [x, y, z, w]\n    Vec4f(const Vec4f& v);                      // Copy constructor\n    Vec4f(const Vec3f& v, float w);              // Homogeneous 3D vector\n\n    Vec4f(VLBlock     b);                       // vl_0, vl_1, ...\n    Vec4f(VLAxis      a, float s = vl_1);        // vl_x, vl_y\n    Vec4f(VLMinusAxis a, float s = vl_1);        // vl_x, vl_y\n\n    explicit Vec4f(float s);\n    explicit Vec4f(const float v[]);\n\n    template<class T, class U = typename T::IsVec> explicit Vec4f(const T& v);\n\n    // Accessor functions\n    int          Elts() const { return 4; };   // Element count\n\n    float&        operator [] (int i);          // Indexing by row\n    const float&  operator [] (int i) const;    // Indexing by row\n\n    float*        Ref();                        // Return pointer to data\n    const float*  Ref() const;                  // Return pointer to data\n\n    // Assignment operators\n    Vec4f&       operator =  (const Vec4f& a);\n    Vec4f&       operator =  (VLBlock k);\n    Vec4f&       operator =  (VLAxis k);\n    Vec4f&       operator =  (VLMinusAxis k);\n\n    template<class T, class U = typename T::IsVec> Vec4f& operator = (const T& v);\n\n    Vec4f&       operator += (const Vec4f& a);\n    Vec4f&       operator -= (const Vec4f& a);\n    Vec4f&       operator *= (const Vec4f& a);\n    Vec4f&       operator *= (float s);\n    Vec4f&       operator /= (const Vec4f& a);\n    Vec4f&       operator /= (float s);\n\n    // Comparison operators\n    bool         operator == (const Vec4f& a) const; // v == a ?\n    bool         operator != (const Vec4f& a) const; // v != a ?\n    bool         operator <  (const Vec4f& a) const; // All v.i <  a.i?\n    bool         operator >= (const Vec4f& a) const; // All v.i >= a.i?\n\n    // Arithmetic operators\n    Vec4f        operator + (const Vec4f& a) const;  // v + a\n    Vec4f        operator - (const Vec4f& a) const;  // v - a\n    const Vec4f& operator + () const;                // -v\n    Vec4f        operator - () const;                // -v\n    Vec4f        operator * (const Vec4f& a) const;  // v * a (vx * ax, ...)\n    Vec4f        operator * (float s) const;        // v * s\n    Vec4f        operator / (const Vec4f& a) const;  // v / a (vx / ax, ...)\n    Vec4f        operator / (float s) const;        // v / s\n\n    // Initialisers\n    Vec4f&       MakeZero();                        // Zero vector\n    Vec4f&       MakeUnit(int i, float k = vl_one);  // kI[i]\n    Vec4f&       MakeBlock(float k = vl_one);        // All-k vector\n\n    // Conversion\n    Vec2f&       AsVec2();\n    const Vec2f& AsVec2() const;\n    Vec3f&       AsVec3();\n    const Vec3f& AsVec3() const;\n\n    // Data\n    float x;\n    float y;\n    float z;\n    float w;\n};\n\n\n// --- Vec4 operators ---------------------------------------------------------\n\nVec4f operator * (float s, const Vec4f& v);    // s * v\n\nfloat  dot  (const Vec4f& a, const Vec4f& b);  // a . b\nVec4f cross(const Vec4f& a, const Vec4f& b, const Vec4f& c); // 4D cross prod.\nfloat  sqrlen   (const Vec4f& v);              // v . v\n#ifndef VL_NO_REAL\nfloat  len      (const Vec4f& v);              // || v ||\nVec4f norm     (const Vec4f& v);              // v / || v ||\nVec4f norm_safe(const Vec4f& v);              // v / || v ||, handles || v || = 0\nVec4f inv      (const Vec4f& v);              // inverse: 1 / v\nVec3f proj     (const Vec4f& v);              // hom. projection\n#endif\nVec4f abs      (const Vec4f& v);              // abs(v_i)\n\n\n// --- Inlines ----------------------------------------------------------------\n\n\ninline Vec4f::Vec4f()\n{\n}\n\ninline Vec4f::Vec4f(float a, float b, float c, float d) :\n    x(a),\n    y(b),\n    z(c),\n    w(d)\n{}\n\ninline Vec4f::Vec4f(const Vec4f& v) :\n    x(v.x),\n    y(v.y),\n    z(v.z),\n    w(v.w)\n{}\n\ninline Vec4f::Vec4f(const Vec3f& v, float w_in) :\n    x(v.x),\n    y(v.y),\n    z(v.z),\n    w(w_in)\n{\n}\n\ninline Vec4f::Vec4f(float s) :\n    x(s),\n    y(s),\n    z(s),\n    w(s)\n{}\n\ninline Vec4f::Vec4f(const float v[]) :\n    x(v[0]),\n    y(v[1]),\n    z(v[2]),\n    w(v[3])\n{\n}\n\ntemplate<class T, class U> inline Vec4f::Vec4f(const T& v)\n{\n    VL_ASSERT_MSG(Elts() == v.Elts() || v.Elts() == VL_REPEAT, \"(Vec4) Vector sizes don't match\");\n\n    for (int i = 0; i < v.Elts(); i++)\n        (*this)[i] = float(v[i]);\n}\n\ninline float& Vec4f::operator [] (int i)\n{\n    VL_RANGE_MSG(i, 0, 4, \"(Vec4::[i]) index out of range\");\n    return (&x)[i];\n}\n\ninline const float& Vec4f::operator [] (int i) const\n{\n    VL_RANGE_MSG(i, 0, 4, \"(Vec4::[i]) index out of range\");\n    return (&x)[i];\n}\n\ninline float* Vec4f::Ref()\n{\n    return &x;\n}\n\ninline const float* Vec4f::Ref() const\n{\n    return &x;\n}\n\ninline Vec4f& Vec4f::operator = (const Vec4f& v)\n{\n    x = v.x;\n    y = v.y;\n    z = v.z;\n    w = v.w;\n\n    return *this;\n}\n\ntemplate<class T, class U> inline Vec4f& Vec4f::operator = (const T& v)\n{\n    VL_ASSERT_MSG(Elts() == v.Elts() || v.Elts() == VL_REPEAT, \"(Vec4::=) Vector sizes don't match\");\n\n    for (int i = 0; i < Elts(); i++)\n        (*this)[i] = Elt(v[i]);\n\n    return *this;\n}\n\ninline Vec4f& Vec4f::operator += (const Vec4f& v)\n{\n    x += v.x;\n    y += v.y;\n    z += v.z;\n    w += v.w;\n\n    return *this;\n}\n\ninline Vec4f& Vec4f::operator -= (const Vec4f& v)\n{\n    x -= v.x;\n    y -= v.y;\n    z -= v.z;\n    w -= v.w;\n\n    return *this;\n}\n\ninline Vec4f& Vec4f::operator *= (const Vec4f& v)\n{\n    x *= v.x;\n    y *= v.y;\n    z *= v.z;\n    w *= v.w;\n\n    return *this;\n}\n\ninline Vec4f& Vec4f::operator *= (float s)\n{\n    x *= s;\n    y *= s;\n    z *= s;\n    w *= s;\n\n    return *this;\n}\n\ninline Vec4f& Vec4f::operator /= (const Vec4f& v)\n{\n    x /= v.x;\n    y /= v.y;\n    z /= v.z;\n    w /= v.w;\n\n    return *this;\n}\n\ninline Vec4f& Vec4f::operator /= (float s)\n{\n    x /= s;\n    y /= s;\n    z /= s;\n    w /= s;\n\n    return *this;\n}\n\n\ninline Vec4f Vec4f::operator + (const Vec4f& a) const\n{\n    Vec4f result;\n\n    result.x = x + a.x;\n    result.y = y + a.y;\n    result.z = z + a.z;\n    result.w = w + a.w;\n\n    return result;\n}\n\ninline Vec4f Vec4f::operator - (const Vec4f& a) const\n{\n    Vec4f result;\n\n    result.x = x - a.x;\n    result.y = y - a.y;\n    result.z = z - a.z;\n    result.w = w - a.w;\n\n    return result;\n}\n\ninline const Vec4f& Vec4f::operator + () const\n{\n    return *this;\n}\n\ninline Vec4f Vec4f::operator - () const\n{\n    Vec4f result;\n\n    result.x = -x;\n    result.y = -y;\n    result.z = -z;\n    result.w = -w;\n\n    return result;\n}\n\ninline Vec4f Vec4f::operator * (const Vec4f& a) const\n{\n    Vec4f result;\n\n    result.x = x * a.x;\n    result.y = y * a.y;\n    result.z = z * a.z;\n    result.w = w * a.w;\n\n    return result;\n}\n\ninline Vec4f Vec4f::operator * (float s) const\n{\n    Vec4f result;\n\n    result.x = x * s;\n    result.y = y * s;\n    result.z = z * s;\n    result.w = w * s;\n\n    return result;\n}\n\ninline Vec4f Vec4f::operator / (const Vec4f& a) const\n{\n    Vec4f result;\n\n    result.x = x / a.x;\n    result.y = y / a.y;\n    result.z = z / a.z;\n    result.w = w / a.w;\n\n    return result;\n}\n\ninline Vec4f Vec4f::operator / (float s) const\n{\n    Vec4f result;\n\n    result.x = x / s;\n    result.y = y / s;\n    result.z = z / s;\n    result.w = w / s;\n\n    return result;\n}\n\ninline Vec4f operator * (float s, const Vec4f& v)\n{\n    return v * s;\n}\n\ninline Vec4f& Vec4f::MakeZero()\n{\n    x = vl_zero; y = vl_zero; z = vl_zero; w = vl_zero;\n    return *this;\n}\n\ninline Vec4f& Vec4f::MakeBlock(float k)\n{\n    x = k; y = k; z = k; w = k;\n    return *this;\n}\n\n\ninline Vec4f::Vec4f(VLBlock k)\n{\n    MakeBlock(float(k));\n}\n\ninline Vec4f::Vec4f(VLAxis k, float s)\n{\n    MakeUnit(k, s);\n}\n\ninline Vec4f::Vec4f(VLMinusAxis k, float s)\n{\n    MakeUnit(k, -s);\n}\n\ninline Vec4f& Vec4f::operator = (VLBlock k)\n{\n    MakeBlock(float(k));\n    return *this;\n}\n\ninline Vec4f& Vec4f::operator = (VLAxis k)\n{\n    MakeUnit(k);\n    return *this;\n}\n\ninline Vec4f& Vec4f::operator = (VLMinusAxis k)\n{\n    MakeUnit(k, vl_minus_one);\n    return *this;\n}\n\ninline Vec2f& Vec4f::AsVec2()\n{\n    return (Vec2f&) *this;\n}\n\ninline const Vec2f& Vec4f::AsVec2() const\n{\n    return (const Vec2f&) *this;\n}\n\ninline Vec3f& Vec4f::AsVec3()\n{\n    return (Vec3f&) *this;\n}\n\ninline const Vec3f& Vec4f::AsVec3() const\n{\n    return (const Vec3f&) *this;\n}\n\n\n\ninline float dot(const Vec4f& a, const Vec4f& b)\n{\n    return a.x * b.x + a.y * b.y + a.z * b.z + a.w * b.w;\n}\n\ninline float sqrlen(const Vec4f& v)\n{\n    return dot(v, v);\n}\n\n#ifndef VL_NO_REAL\ninline float len(const Vec4f& v)\n{\n    return sqrt(dot(v, v));\n}\n\ninline Vec4f norm(const Vec4f& v)\n{\n    VL_ASSERT_MSG(sqrlen(v) > vl_zero, \"normalising length-zero vector\");\n    return v / len(v);\n}\n\ninline Vec4f norm_safe(const Vec4f& v)\n{\n    return v / (len(v) + float(1e-8));\n}\n\ninline Vec4f inv(const Vec4f& v)\n{\n    return Vec4f(float(1) / v.x, float(1) / v.y, float(1) / v.z, float(1) / v.w);\n}\n#endif\n\ninline Vec4f abs(const Vec4f& v)\n{\n    return Vec4f(abs(v.x), abs(v.y), abs(v.z), abs(v.w));\n}\n\n#endif\n\n#ifndef VLF_MAT2_H\n#define VLF_MAT2_H\n\n\n\n// --- Mat2 Class -------------------------------------------------------------\n\nclass Mat2f : public VLMatType\n{\npublic:\n    typedef Vec2f Vec;\n\n    // Constructors\n    Mat2f();\n    Mat2f(float a, float b, float c, float d);\n    Mat2f(const Vec2f v0, const Vec2f v1);\n    Mat2f(const Mat2f& m);\n\n    Mat2f(Vec2f diagonal);\n    Mat2f(VLDiag k);\n    Mat2f(VLBlock k);\n\n    explicit Mat2f(const float m[4]);\n\n    template<class T, class U = typename T::IsMat> explicit Mat2f(const T& v);\n\n    // Accessor functions\n    int          Elts() const { return 4; };\n    int          Rows() const { return 2; };\n    int          Cols() const { return 2; };\n\n    Vec2f&       operator [] (int i);\n    const Vec2f& operator [] (int i) const;\n\n    float&        operator () (int i, int j);\n    float         operator () (int i, int j) const;\n\n    float*        Ref();\n    const float*  Ref() const;\n\n    // Assignment operators\n    Mat2f&       operator =  (const Mat2f& m);\n    Mat2f&       operator =  (VLDiag k);\n    Mat2f&       operator =  (VLBlock k);\n\n    template<class T, class U = typename T::IsMat> Mat2f& operator = (const T& m);\n\n    Mat2f&       operator += (const Mat2f& m);\n    Mat2f&       operator -= (const Mat2f& m);\n    Mat2f&       operator *= (const Mat2f& m);\n    Mat2f&       operator *= (float s);\n    Mat2f&       operator /= (float s);\n\n    // Comparison operators\n    bool         operator == (const Mat2f& m) const; // M == N?\n    bool         operator != (const Mat2f& m) const; // M != N?\n\n    // Arithmetic operators\n    Mat2f        operator + (const Mat2f& m) const;  // M + N\n    Mat2f        operator - (const Mat2f& m) const;  // M - N\n    const Mat2f& operator + () const;                // +M\n    Mat2f        operator - () const;                // -M\n    Mat2f        operator * (const Mat2f& m) const;  // M * N\n    Mat2f        operator * (float s) const;          // M * s\n    Mat2f        operator / (float s) const;          // M / s\n\n    // Initialisers\n    void         MakeZero();                         // Zero matrix\n    void         MakeIdentity();                     // Identity matrix\n    void         MakeDiag (Vec2f d);                 // Diagonal = d, 0 otherwise\n    void         MakeDiag (float k = vl_one);         // Diagonal = k, 0 otherwise\n    void         MakeBlock(float k = vl_one);         // all elts = k\n\n    // Data\n    Vec2f x;\n    Vec2f y;\n};\n\n\n// --- Matrix operators -------------------------------------------------------\n\nVec2f& operator *= (Vec2f& v, const Mat2f& m);        // v *= m\nVec2f  operator *  (const Mat2f& m, const Vec2f& v);  // m * v\nVec2f  operator *  (const Vec2f& v, const Mat2f& m);  // v * m\nMat2f  operator *  (const float   s, const Mat2f& m);  // s * m\n\nVec2f  row(const Mat2f& m, int i);             // Return row i of 'm' (same as m[i])\nVec2f  col(const Mat2f& m, int j);             // Return column i of 'm'\n\nMat2f  trans(const Mat2f& m);                  // Transpose\nfloat   trace(const Mat2f& m);                  // Trace\nMat2f  adj  (const Mat2f& m);                  // Adjoint\nfloat   det  (const Mat2f& m);                  // Determinant\n#ifndef VL_NO_REAL\nMat2f  inv  (const Mat2f& m);                  // Inverse\n#endif\nMat2f  abs  (const Mat2f& m);                  // abs(m_ij)\nMat2f  oprod(const Vec2f& a, const Vec2f& b);  // Outer product\n\n// The xform functions help avoid dependence on whether row or column\n// vectors are used to represent points and vectors.\nVec2f  xform(const Mat2f& m, const Vec2f& v);  // Transform of v by m\nMat2f  xform(const Mat2f& m, const Mat2f& n);  // Xform v -> m(n(v))\n\n\n// --- Inlines ----------------------------------------------------------------\n\n\ninline Mat2f::Mat2f()\n{\n}\n\ninline Mat2f::Mat2f(float a, float b, float c, float d) :\n    x(a, b),\n    y(c, d)\n{\n}\n\ninline Mat2f::Mat2f(Vec2f v0, Vec2f v1) :\n    x(v0),\n    y(v1)\n{\n}\n\ninline Mat2f::Mat2f(const Mat2f& m) :\n    x(m.x),\n    y(m.y)\n{\n}\n\ninline Mat2f::Mat2f(Vec2f diag)\n{\n    MakeDiag(diag);\n}\n\ninline Mat2f::Mat2f(VLDiag k)\n{\n    MakeDiag(float(k));\n}\n\ninline Mat2f::Mat2f(VLBlock k)\n{\n    MakeBlock(float(k));\n}\n\ninline Mat2f::Mat2f(const float m[4])\n{\n    float* elts = (float*) this;\n    for (int i = 0; i < 4; i++)\n        *elts++ = *m++;\n}\n\ntemplate<class T, class U> inline Mat2f::Mat2f(const T& m)\n{\n    VL_ASSERT_MSG(m.Rows() == Rows() || m.Rows() == VL_REPEAT, \"(Mat2) Matrix rows don't match\");\n\n    for (int i = 0; i < Rows(); i++)\n        (*this)[i] = m[i];\n}\n\ninline Vec2f& Mat2f::operator [] (int i)\n{\n    VL_RANGE_MSG(i, 0, 2, \"(Mat2::[i]) index out of range\");\n    return (&x)[i];\n}\n\ninline const Vec2f& Mat2f::operator [] (int i) const\n{\n    VL_RANGE_MSG(i, 0, 2, \"(Mat2::[i]) index out of range\");\n    return (&x)[i];\n}\n\ninline float& Mat2f::operator () (int i, int j)\n{\n    VL_RANGE_MSG(i, 0, 2, \"(Mat2::(i,j)) index out of range\");\n    VL_RANGE_MSG(j, 0, 2, \"(Mat2::(i,j)) index out of range\");\n\n    return (&x)[i][j];\n}\n\ninline float Mat2f::operator () (int i, int j) const\n{\n    VL_RANGE_MSG(i, 0, 2, \"(Mat2::(i,j)) index out of range\");\n    VL_RANGE_MSG(j, 0, 2, \"(Mat2::(i,j)) index out of range\");\n\n    return (&x)[i][j];\n}\n\ninline float* Mat2f::Ref()\n{\n    return &x.x;\n}\n\ninline const float* Mat2f::Ref() const\n{\n    return &x.x;\n}\n\ninline void Mat2f::MakeZero()\n{\n    x.x = vl_zero; x.y = vl_zero;\n    y.x = vl_zero; y.y = vl_zero;\n}\n\ninline void Mat2f::MakeDiag(float k)\n{\n    x.x = k;          x.y = vl_zero;\n    y.x = vl_zero;    y.y = k;\n}\n\ninline void Mat2f::MakeDiag(Vec2f d)\n{\n    x.x = d.x;        x.y = vl_zero;\n    y.x = vl_zero;    y.y = d.y;\n}\n\ninline void Mat2f::MakeIdentity()\n{\n    x.x = vl_one;     x.y = vl_zero;\n    y.x = vl_zero;    y.y = vl_one;\n}\n\ninline void Mat2f::MakeBlock(float k)\n{\n    x.x = k; x.y = k;\n    y.x = k; y.y = k;\n}\n\ninline Mat2f& Mat2f::operator = (VLDiag k)\n{\n    MakeDiag(float(k));\n    return *this;\n}\n\ninline Mat2f& Mat2f::operator = (VLBlock k)\n{\n    MakeBlock(float(k));\n    return *this;\n}\n\ninline Mat2f& Mat2f::operator = (const Mat2f& m)\n{\n    x = m.x;\n    y = m.y;\n\n    return *this;\n}\n\ntemplate<class T, class U> inline Mat2f& Mat2f::operator = (const T& m)\n{\n    VL_ASSERT_MSG(m.Rows() == Rows() || m.Rows() == VL_REPEAT, \"(Mat2::=) Matrix rows don't match\");\n\n    for (int i = 0; i < Rows(); i++)\n        (*this)[i] = m[i];\n\n    return *this;\n}\n\ninline Mat2f& Mat2f::operator += (const Mat2f& m)\n{\n    x += m.x;\n    y += m.y;\n\n    return *this;\n}\n\ninline Mat2f& Mat2f::operator -= (const Mat2f& m)\n{\n    x -= m.x;\n    y -= m.y;\n\n    return *this;\n}\n\ninline Mat2f& Mat2f::operator *= (const Mat2f& m)\n{\n    Vec2f t;\n\n    t = x.x * m.x + x.y * m.y;\n    y = y.x * m.x + y.y * m.y;\n    x = t;\n\n    return *this;\n}\n\ninline Mat2f& Mat2f::operator *= (float s)\n{\n    x *= s;\n    y *= s;\n\n    return *this;\n}\n\ninline Mat2f& Mat2f::operator /= (float s)\n{\n    x /= s;\n    y /= s;\n\n    return *this;\n}\n\n\ninline Mat2f Mat2f::operator + (const Mat2f& m) const\n{\n    Mat2f result;\n\n    result.x = x + m.x;\n    result.y = y + m.y;\n\n    return result;\n}\n\ninline Mat2f Mat2f::operator - (const Mat2f& m) const\n{\n    Mat2f result;\n\n    result.x = x - m.x;\n    result.y = y - m.y;\n\n    return result;\n}\n\ninline const Mat2f& Mat2f::operator + () const\n{\n    return *this;\n}\n\ninline Mat2f Mat2f::operator - () const\n{\n    Mat2f result;\n\n    result.x = -x;\n    result.y = -y;\n\n    return result;\n}\n\ninline Mat2f Mat2f::operator * (const Mat2f& m) const\n{\n    Mat2f result;\n\n    result.x.x = x.x * m.x.x + x.y * m.y.x;\n    result.x.y = x.x * m.x.y + x.y * m.y.y;\n    result.y.x = y.x * m.x.x + y.y * m.y.x;\n    result.y.y = y.x * m.x.y + y.y * m.y.y;\n\n    return result;\n}\n\ninline Mat2f Mat2f::operator * (float s) const\n{\n    Mat2f result;\n\n    result.x = x * s;\n    result.y = y * s;\n\n    return result;\n}\n\ninline Mat2f Mat2f::operator / (float s) const\n{\n    Mat2f result;\n\n    result.x = x / s;\n    result.y = y / s;\n\n    return result;\n}\n\ninline Mat2f  operator *  (float s, const Mat2f& m)\n{\n    return m * s;\n}\n\ninline Vec2f operator * (const Mat2f& m, const Vec2f& v)\n{\n    Vec2f result;\n\n    result.x = m.x.x * v.x + m.x.y * v.y;\n    result.y = m.y.x * v.x + m.y.y * v.y;\n\n    return result;\n}\n\ninline Vec2f operator * (const Vec2f& v, const Mat2f& m)\n{\n    Vec2f result;\n\n    result.x = v.x * m.x.x + v.y * m.y.x;\n    result.y = v.x * m.x.y + v.y * m.y.y;\n\n    return result;\n}\n\ninline Vec2f& operator *= (Vec2f& v, const Mat2f& m)\n{\n    float t;\n\n    t   = v.x * m.x.x + v.y * m.y.x;\n    v.y = v.x * m.x.y + v.y * m.y.y;\n    v.x = t;\n\n    return v;\n}\n\n\ninline Vec2f row(const Mat2f& m, int i)\n{\n    VL_INDEX(i, 2);\n    return Vec2f(*(&m.x + i));\n}\n\ninline Vec2f col(const Mat2f& m, int j)\n{\n    VL_INDEX(j, 2);\n    return Vec2f(m.x[j], m.y[j]);\n}\n\ninline Mat2f trans(const Mat2f& m)\n{\n    Mat2f result;\n\n    result.x.x = m.x.x; result.x.y = m.y.x;\n    result.y.x = m.x.y; result.y.y = m.y.y;\n\n    return result;\n}\n\ninline float trace(const Mat2f& m)\n{\n    return m.x.x + m.y.y;\n}\n\ninline Mat2f adj(const Mat2f& m)\n{\n    Mat2f result;\n\n    result.x = -cross(m.y);\n    result.y =  cross(m.x);\n\n    return result;\n}\n\n#endif\n\n#ifndef VLF_MAT3_H\n#define VLF_MAT3_H\n\n\n\n// --- Mat3 Class -------------------------------------------------------------\n\nclass Vec4f;\n\nclass Mat3f : public VLMatType\n{\npublic:\n    typedef Vec3f Vec;\n\n    // Constructors\n    Mat3f();\n    Mat3f(float a, float b, float c,\n          float d, float e, float f,\n          float g, float h, float i);\n    Mat3f(Vec3f v0, Vec3f v1, Vec3f v2);\n    Mat3f(const Mat3f& m);\n\n    Mat3f(Vec3f diagonal);\n    Mat3f(VLDiag k);\n    Mat3f(VLBlock k);\n\n    explicit Mat3f(const Mat2f& m, float scale = float(vl_1));\n    explicit Mat3f(const float m[9]);\n\n    template<class T, class U = typename T::IsMat> explicit Mat3f(const T& v);\n\n    // Accessor functions\n    int          Elts() const { return 9; };\n    int          Rows() const { return 3; };\n    int          Cols() const { return 3; };\n\n    Vec3f&       operator [] (int i);\n    const Vec3f& operator [] (int i) const;\n\n    float&        operator () (int i, int j);\n    float         operator () (int i, int j) const;\n\n    float*        Ref();\n    const float*  Ref() const;\n\n    // Assignment operators\n    Mat3f&       operator =  (const Mat3f& m);\n    Mat3f&       operator =  (VLDiag k);\n    Mat3f&       operator =  (VLBlock k);\n\n    template<class T, class U = typename T::IsMat> Mat3f& operator = (const T& m);\n\n    Mat3f&       operator += (const Mat3f& m);\n    Mat3f&       operator -= (const Mat3f& m);\n    Mat3f&       operator *= (const Mat3f& m);\n    Mat3f&       operator *= (float s);\n    Mat3f&       operator /= (float s);\n\n    // Comparison operators\n    bool         operator == (const Mat3f& m) const; // M == N?\n    bool         operator != (const Mat3f& m) const; // M != N?\n\n    // Arithmetic operators\n    Mat3f        operator + (const Mat3f& m) const;  // M + N\n    Mat3f        operator - (const Mat3f& m) const;  // M - N\n    const Mat3f& operator + () const;                // +M\n    Mat3f        operator - () const;                // -M\n    Mat3f        operator * (const Mat3f& m) const;  // M * N\n    Mat3f        operator * (float s) const;          // M * s\n    Mat3f        operator / (float s) const;          // M / s\n\n    // Initialisers\n    void         MakeZero();                         // Zero matrix\n    void         MakeIdentity();                     // Identity matrix\n    void         MakeDiag (Vec3f d);                 // Diagonal = d, 0 otherwise\n    void         MakeDiag (float k = vl_one);         // Diagonal = k, 0 otherwise\n    void         MakeBlock(float k = vl_one);         // all elts = k\n\n    // Conversion\n    Mat2f        AsMat2() const;\n\n    // Data\n    Vec3f x;\n    Vec3f y;\n    Vec3f z;\n};\n\n\n// --- Matrix operators -------------------------------------------------------\n\nVec3f& operator *= (Vec3f& v, const Mat3f& m);        // v *= m\nVec3f  operator *  (const Mat3f& m, const Vec3f& v);  // m * v\nVec3f  operator *  (const Vec3f& v, const Mat3f& m);  // v * m\nMat3f  operator *  (const float   s, const Mat3f& m);  // s * m\n\nVec3f  row(const Mat3f& m, int i);             // Return row i of 'm' (same as m[i])\nVec3f  col(const Mat3f& m, int j);             // Return column i of 'm'\n\nMat3f  trans(const Mat3f& m);                  // Transpose\nfloat   trace(const Mat3f& m);                  // Trace\nMat3f  adj  (const Mat3f& m);                  // Adjoint\nfloat   det  (const Mat3f& m);                  // Determinant\n#ifndef VL_NO_REAL\nMat3f  inv  (const Mat3f& m);                  // Inverse\n#endif\nMat3f  abs  (const Mat3f& m);                  // abs(m_ij)\nMat3f  oprod(const Vec3f& a, const Vec3f& b);  // Outer product\n\n// The xform functions help avoid dependence on whether row or column\n// vectors are used to represent points and vectors.\nVec3f  xform(const Mat3f& m, const Vec3f& v);  // Transform of v by m\nVec2f  xform(const Mat3f& m, const Vec2f& v);  // Hom. xform of v by m\nMat3f  xform(const Mat3f& m, const Mat3f& n);  // Xform v -> m(n(v))\n\n\n// --- Inlines ----------------------------------------------------------------\n\ninline Mat3f::Mat3f()\n{\n}\n\ninline Mat3f::Mat3f\n(\n    float a, float b, float c,\n    float d, float e, float f,\n    float g, float h, float i\n) :\n    x(a, b, c),\n    y(d, e, f),\n    z(g, h, i)\n{\n}\n\ninline Mat3f::Mat3f(Vec3f v0, Vec3f v1, Vec3f v2) :\n    x(v0),\n    y(v1),\n    z(v2)\n{\n}\n\ninline Mat3f::Mat3f(const Mat3f& m) :\n    x(m.x),\n    y(m.y),\n    z(m.z)\n{\n}\n\ninline Mat3f::Mat3f(Vec3f d)\n{\n    MakeDiag(d);\n}\n\ninline Mat3f::Mat3f(VLDiag k)\n{\n    MakeDiag(float(k));\n}\n\ninline Mat3f::Mat3f(VLBlock k)\n{\n    MakeBlock(float(k));\n}\n\ninline Mat3f::Mat3f(const float m[9])\n{\n    float* elts = (float*) this;\n    for (int i = 0; i < 9; i++)\n        *elts++ = *m++;\n}\n\ntemplate<class T, class U> inline Mat3f::Mat3f(const T& m)\n{\n    VL_ASSERT_MSG(m.Rows() == Rows() || m.Rows() == VL_REPEAT, \"(Mat3) Matrix rows don't match\");\n\n    for (int i = 0; i < Rows(); i++)\n        (*this)[i] = m[i];\n}\n\ninline Vec3f& Mat3f::operator [] (int i)\n{\n    VL_RANGE_MSG(i, 0, 3, \"(Mat3::[i]) index out of range\");\n    return (&x)[i];\n}\n\ninline const Vec3f& Mat3f::operator [] (int i) const\n{\n    VL_RANGE_MSG(i, 0, 3, \"(Mat3::[i]) index out of range\");\n    return (&x)[i];\n}\n\ninline float& Mat3f::operator () (int i, int j)\n{\n    VL_RANGE_MSG(i, 0, 3, \"(Mat2::(i,j)) index out of range\");\n    VL_RANGE_MSG(j, 0, 3, \"(Mat2::(i,j)) index out of range\");\n\n    return (&x)[i][j];\n}\n\ninline float Mat3f::operator () (int i, int j) const\n{\n    VL_RANGE_MSG(i, 0, 3, \"(Mat2::(i,j)) index out of range\");\n    VL_RANGE_MSG(j, 0, 3, \"(Mat2::(i,j)) index out of range\");\n\n    return (&x)[i][j];\n}\n\ninline float* Mat3f::Ref()\n{\n    return &x.x;\n}\n\ninline const float* Mat3f::Ref() const\n{\n    return &x.x;\n}\n\ninline void Mat3f::MakeZero()\n{\n    x.x = vl_zero; x.y = vl_zero; x.z = vl_zero;\n    y.x = vl_zero; y.y = vl_zero; y.z = vl_zero;\n    z.x = vl_zero; z.y = vl_zero; z.z = vl_zero;\n}\n\ninline void Mat3f::MakeIdentity()\n{\n    x.x = vl_one;     x.y = vl_zero;    x.z = vl_zero;\n    y.x = vl_zero;    y.y = vl_one;     y.z = vl_zero;\n    z.x = vl_zero;    z.y = vl_zero;    z.z = vl_one;\n}\n\ninline void Mat3f::MakeDiag(Vec3f d)\n{\n    x.x = d.x;        x.y = vl_zero;    x.z = vl_zero;\n    y.x = vl_zero;    y.y = d.y;        y.z = vl_zero;\n    z.x = vl_zero;    z.y = vl_zero;    z.z = d.z;\n}\n\ninline void Mat3f::MakeDiag(float k)\n{\n    x.x = k;          x.y = vl_zero;    x.z = vl_zero;\n    y.x = vl_zero;    y.y = k;          y.z = vl_zero;\n    z.x = vl_zero;    z.y = vl_zero;    z.z = k;\n}\n\ninline void Mat3f::MakeBlock(float k)\n{\n    x.x = k; x.y = k; x.z = k;\n    y.x = k; y.y = k; y.z = k;\n    z.x = k; z.y = k; z.z = k;\n}\n\ninline Mat2f Mat3f::AsMat2() const\n{\n    return Mat2f(x.AsVec2(), y.AsVec2());\n}\n\ninline Mat3f& Mat3f::operator = (VLDiag k)\n{\n    MakeDiag(float(k));\n    return *this;\n}\n\ninline Mat3f& Mat3f::operator = (VLBlock k)\n{\n    MakeBlock(float(k));\n    return *this;\n}\n\ntemplate<class T, class U> inline Mat3f& Mat3f::operator = (const T& m)\n{\n   VL_ASSERT_MSG(m.Rows() == Rows()|| m.Rows() == VL_REPEAT, \"(Mat3::=) Matrix rows don't match\");\n\n   for (int i = 0; i < Rows(); i++)\n       (*this)[i] = m[i];\n\n   return *this;\n}\n\ninline const Mat3f& Mat3f::operator + () const\n{\n    return *this;\n}\n\ninline Mat3f operator *  (const float s, const Mat3f& m)\n{\n    return m * s;\n}\n\ninline Vec3f operator * (const Mat3f& m, const Vec3f& v)\n{\n    Vec3f result;\n\n    result.x = v.x * m.x.x + v.y * m.x.y + v.z * m.x.z;\n    result.y = v.x * m.y.x + v.y * m.y.y + v.z * m.y.z;\n    result.z = v.x * m.z.x + v.y * m.z.y + v.z * m.z.z;\n\n    return result;\n}\n\ninline Vec3f operator * (const Vec3f& v, const Mat3f& m)\n{\n    Vec3f result;\n\n    result.x = v.x * m.x.x + v.y * m.y.x + v.z * m.z.x;\n    result.y = v.x * m.x.y + v.y * m.y.y + v.z * m.z.y;\n    result.z = v.x * m.x.z + v.y * m.y.z + v.z * m.z.z;\n\n    return result;\n}\n\ninline Vec3f& operator *= (Vec3f& v, const Mat3f& m)\n{\n    float t0, t1;\n\n    t0  = v.x * m.x.x + v.y * m.y.x + v.z * m.z.x;\n    t1  = v.x * m.x.y + v.y * m.y.y + v.z * m.z.y;\n    v.z = v.x * m.x.z + v.y * m.y.z + v.z * m.z.z;\n    v.x = t0;\n    v.y = t1;\n\n    return v;\n}\n\ninline Vec3f row(const Mat3f& m, int i)\n{\n    VL_INDEX(i, 3);\n    return Vec3f(*(&m.x + i));\n}\n\ninline Vec3f col(const Mat3f& m, int j)\n{\n    VL_INDEX(j, 3);\n    return Vec3f(m.x[j], m.y[j], m.z[j]);\n}\n\n#endif\n\n\n#ifndef VLF_MAT4_H\n#define VLF_MAT4_H\n\n\n\n// --- Mat4 Class -------------------------------------------------------------\n\nclass Vec3f;\n\nclass Mat4f : public VLMatType\n{\npublic:\n    typedef Vec4f Vec;\n\n    // Constructors\n    Mat4f();\n    Mat4f(float a, float b, float c, float d,\n          float e, float f, float g, float h,\n          float i, float j, float k, float l,\n          float m, float n, float o, float p);\n    Mat4f(Vec4f v0, Vec4f v1, Vec4f v2, Vec4f v3);\n    Mat4f(const Mat4f& m);\n\n    Mat4f(Vec4f diagonal);\n    Mat4f(VLDiag k);\n    Mat4f(VLBlock k);\n\n    explicit Mat4f(const Mat3f& m, float scale = float(vl_1));\n    explicit Mat4f(const float data[16]);\n\n    template<class T, class U = typename T::IsMat> explicit Mat4f(const T& v);\n\n    // Accessor functions\n    int          Elts() const { return 16; };\n    int          Rows() const { return  4; };\n    int          Cols() const { return  4; };\n\n    Vec4f&       operator [] (int i);\n    const Vec4f& operator [] (int i) const;\n\n    float&        operator () (int i, int j);\n    float         operator () (int i, int j) const;\n\n    float*        Ref();\n    const float*  Ref() const;\n\n    // Assignment operators\n    Mat4f&       operator =  (const Mat4f& m);\n    Mat4f&       operator =  (VLDiag k);\n    Mat4f&       operator =  (VLBlock k);\n\n    template<class T, class U = typename T::IsMat> Mat4f& operator = (const T& m);\n\n    Mat4f&       operator += (const Mat4f& m);\n    Mat4f&       operator -= (const Mat4f& m);\n    Mat4f&       operator *= (const Mat4f& m);\n    Mat4f&       operator *= (float s);\n    Mat4f&       operator /= (float s);\n\n    // Comparison operators\n    bool         operator == (const Mat4f& m) const; // M == N?\n    bool         operator != (const Mat4f& m) const; // M != N?\n\n    // Arithmetic operators\n    Mat4f        operator + (const Mat4f& m) const;  // M + N\n    Mat4f        operator - (const Mat4f& m) const;  // M - N\n    const Mat4f& operator + () const;                // +M\n    Mat4f        operator - () const;                // -M\n    Mat4f        operator * (const Mat4f& m) const;  // M * N\n    Mat4f        operator * (float s) const;          // M * s\n    Mat4f        operator / (float s) const;          // M / s\n\n    // Initialisers\n    void         MakeZero();                         // Zero matrix\n    void         MakeIdentity();                     // Identity matrix\n    void         MakeDiag (Vec4f d);                 // Diagonal = d, 0 otherwise\n    void         MakeDiag (float k = vl_one);         // Diagonal = k, 0 otherwise\n    void         MakeBlock(float k = vl_one);         // All elts = k\n\n    // Conversion\n    Mat3f        AsMat3() const;\n\n    // Data\n    Vec4f x;\n    Vec4f y;\n    Vec4f z;\n    Vec4f w;\n};\n\n\n// --- Matrix operators -------------------------------------------------------\n\nVec4f& operator *= (Vec4f& a, const Mat4f& m);        // v *= m\nVec4f  operator *  (const Mat4f& m, const Vec4f& v);  // m * v\nVec4f  operator *  (const Vec4f& v, const Mat4f& m);  // v * m\nMat4f  operator *  (const float   s, const Mat4f& m);  // s * m\n\nVec4f  row(const Mat4f& m, int i);             // Return row i of 'm' (same as m[i])\nVec4f  col(const Mat4f& m, int j);             // Return column i of 'm'\n\nMat4f  trans(const Mat4f& m);                  // Transpose\nfloat   trace(const Mat4f& m);                  // Trace\nMat4f  adj  (const Mat4f& m);                  // Adjoint\nfloat   det  (const Mat4f& m);                  // Determinant\n#ifndef VL_NO_REAL\nMat4f  inv  (const Mat4f& m);                  // Inverse\n#endif\nMat4f  abs  (const Mat4f& m);                  // abs(m_ij)\nMat4f  oprod(const Vec4f& a, const Vec4f& b);  // Outer product\n\n// The xform functions help avoid dependence on whether row or column\n// vectors are used to represent points and vectors.\nVec4f  xform(const Mat4f& m, const Vec4f& v);  // Transform of v by m\nVec3f  xform(const Mat4f& m, const Vec3f& v);  // Hom. xform of v by m\nMat4f  xform(const Mat4f& m, const Mat4f& n);  // Xform v -> m(n(v))\n\n\n// --- Inlines ----------------------------------------------------------------\n\ninline Mat4f::Mat4f()\n{\n}\n\ninline Mat4f::Mat4f(Vec4f v0, Vec4f v1, Vec4f v2, Vec4f v3) :\n    x(v0),\n    y(v1),\n    z(v2),\n    w(v3)\n{\n}\n\ninline Mat4f::Mat4f(const Mat4f& m) :\n    x(m.x),\n    y(m.y),\n    z(m.z),\n    w(m.w)\n{\n}\n\ninline Mat4f::Mat4f(Vec4f d)\n{\n    MakeDiag(d);\n}\n\ninline Mat4f::Mat4f(VLDiag k)\n{\n    MakeDiag(float(k));\n}\n\ninline Mat4f::Mat4f(VLBlock k)\n{\n    MakeBlock(float(k));\n}\n\ninline Mat4f::Mat4f(const float m[16])\n{\n    float* elts = (float*) this;\n    for (int i = 0; i < 16; i++)\n        *elts++ = *m++;\n}\n\ntemplate<class T, class U> inline Mat4f::Mat4f(const T& m)\n{\n    VL_ASSERT_MSG(m.Rows() == Rows() || m.Rows() == VL_REPEAT, \"(Mat4) Matrix rows don't match\");\n\n    for (int i = 0; i < Rows(); i++)\n        (*this)[i] = m[i];\n}\n\ninline Vec4f& Mat4f::operator [] (int i)\n{\n    VL_RANGE_MSG(i, 0, 4, \"(Mat4::[i]) index out of range\");\n    return (&x)[i];\n}\n\ninline const Vec4f& Mat4f::operator [] (int i) const\n{\n    VL_RANGE_MSG(i, 0, 4, \"(Mat4::[i]) index out of range\");\n    return (&x)[i];\n}\n\ninline float& Mat4f::operator () (int i, int j)\n{\n    VL_RANGE_MSG(i, 0, 4, \"(Mat2::(i,j)) index out of range\");\n    VL_RANGE_MSG(j, 0, 4, \"(Mat2::(i,j)) index out of range\");\n\n    return (&x)[i][j];\n}\n\ninline float Mat4f::operator () (int i, int j) const\n{\n    VL_RANGE_MSG(i, 0, 4, \"(Mat2::(i,j)) index out of range\");\n    VL_RANGE_MSG(j, 0, 4, \"(Mat2::(i,j)) index out of range\");\n\n    return (&x)[i][j];\n}\n\ninline float* Mat4f::Ref()\n{\n    return &x.x;\n}\n\ninline const float* Mat4f::Ref() const\n{\n    return &x.x;\n}\n\ninline Mat4f& Mat4f::operator = (VLDiag k)\n{\n    MakeDiag(float(k));\n    return *this;\n}\n\ninline Mat4f& Mat4f::operator = (VLBlock k)\n{\n    MakeBlock(float(k));\n    return *this;\n}\n\ntemplate<class T, class U> inline Mat4f& Mat4f::operator = (const T& m)\n{\n    VL_ASSERT_MSG(Rows() == m.Rows() || m.Rows() == VL_REPEAT, \"(Mat4::=) Matrix rows don't match\");\n\n    for (int i = 0; i < Rows(); i++)\n        (*this)[i] = m[i];\n\n    return *this;\n}\n\ninline const Mat4f& Mat4f::operator + () const\n{\n    return *this;\n}\n\ninline Mat4f operator * (float s, const Mat4f& m)\n{\n    return m * s;\n}\n\ninline Vec4f row(const Mat4f& m, int i)\n{\n    VL_INDEX(i, 4);\n    return Vec4f(*(&m.x + i));\n}\n\ninline Vec4f col(const Mat4f& m, int j)\n{\n    VL_INDEX(j, 4);\n    return Vec4f(m.x[j], m.y[j], m.z[j], m.w[j]);\n}\n\n#endif\n\n\n#ifndef VL_TRANSFORM_H\n#define VL_TRANSFORM_H\n\nMat2f CRot2f   (float theta);\nMat2f RRot2f   (float theta);\nMat2f Scale2f  (const Vec2f& s);\n\n// Note: all rotations are right-handed.\nMat3f Scale3f  (const Vec3f& s);                     // Scales vector by 's'\nMat3f CRot3f   (const Vec3f& axis, float theta);      // Rotate col vector around axis by theta in radians.\nMat3f RRot3f   (const Vec3f& axis, float theta);      // Rotate row vector around axis by theta in radians\nMat3f CRot3f   (VLAxis       axis, float theta);      // Rotate col vector around axis by theta in radians.\nMat3f RRot3f   (VLAxis       axis, float theta);      // Rotate row vector around axis by theta in radians\nMat3f CRot3f   (const Vec4f& q);                     // Rotate col vector using given quaternion\nMat3f RRot3f   (const Vec4f& q);                     // Rotate row vector using given quaternion\nMat3f CRot3f   (const Vec3f& from, const Vec3f& to); // Rotates col vector 'from' to vector 'to'\nMat3f RRot3f   (const Vec3f& from, const Vec3f& to); // Rotates row vector 'from' to vector 'to'\n\nMat3f HScale3f (const Vec2f& s);     // Scale2f as 3x3 homogeneous matrix\nMat3f HCRot3f  (float theta);         // Rot2f as 3x3 homogeneous matrix on col vectors: see 'proj'.\nMat3f HRRot3f  (float theta);         // Rot2f as 3x3 homogeneous matrix on row vectors: see 'proj'\nMat3f HCTrans3f(const Vec2f& t);     // Given 2d translation as 3x3 homogeneous matrix on col vectors\nMat3f HRTrans3f(const Vec2f& t);     // Given 2d translation as 3x3 homogeneous matrix on row vectors\n\nMat4f HScale4f (const Vec3f& s);     // Scale3f as 4x4 homogeneous matrix\nMat4f HCRot4f  (const Vec3f& axis, float theta);       // CRot3f as 4x4 homogeneous matrix\nMat4f HRRot4f  (const Vec3f& axis, float theta);       // RRot3f as 4x4 homogeneous matrix\nMat4f HCRot4f  (VLAxis       axis, float theta);       // CRot3f as 4x4 homogeneous matrix\nMat4f HRRot4f  (VLAxis       axis, float theta);       // RRot3f as 4x4 homogeneous matrix\nMat4f HCRot4f  (const Vec4f& q);                      // CRot3f as 4x4 homogeneous matrix\nMat4f HRRot4f  (const Vec4f& q);                      // RRot3f as 4x4 homogeneous matrix\nMat4f HCRot4f  (const Vec3f& from, const Vec3f& to);  // CRot3f as 4x4 homogeneous matrix\nMat4f HRRot4f  (const Vec3f& from, const Vec3f& to);  // RRot3f as 4x4 homogeneous matrix\nMat4f HCTrans4f(const Vec3f& t);     // Given 3d translation as 4x4 homogeneous matrix on col vectors\nMat4f HRTrans4f(const Vec3f& t);     // Given 3d translation as 4x4 homogeneous matrix on row vectors\n\n// Legacy, strongly recommended you use explicit RRot/CRot calls.\n#ifdef VL_ROW_ORIENT\ninline Mat2f Rot2f(float theta)                            { return RRot2f(theta); }\n\ninline Mat3f Rot3f(const Vec3f& axis, float theta)         { return RRot3f(axis, theta); }\ninline Mat3f Rot3f(const Vec4f& quaternion)               { return RRot3f(quaternion); }\ninline Mat3f Rot3f(const Vec3f& from, const Vec3f& to)    { return RRot3f(from, to); }\n\ninline Mat3f HRot3f  (float theta)                         { return HRRot3f(theta); }\ninline Mat3f HTrans3f(const Vec2f& t)                     { return HRTrans3f(t); }\n\ninline Mat4f HRot4f  (const Vec3f& axis, float theta)      { return HRRot4f(axis, theta); }\ninline Mat4f HRot4f  (const Vec4f& q)                     { return HRRot4f(q); }\ninline Mat4f HRot4f  (const Vec3f& from, const Vec3f& to) { return HRRot4f(from, to); }\ninline Mat4f HTrans4f(const Vec3f& t)                     { return HRTrans4f(t); }\n\ninline Vec2f xform(const Mat2f& m, const Vec2f& v)\n{ return v * m; }\ninline Mat2f xform(const Mat2f& m, const Mat2f& n)\n{ return n * m; }\n\ninline Vec2f xform(const Mat3f& m, const Vec2f& v)\n{ return proj(Vec3f(v, float(1)) * m); }\ninline Vec3f xform(const Mat3f& m, const Vec3f& v)\n{ return v * m; }\ninline Mat3f xform(const Mat3f& m, const Mat3f& n)\n{ return n * m; }\n\ninline Vec3f xform(const Mat4f& m, const Vec3f& v)\n{ return proj(Vec4f(v, float(1)) * m); }\ninline Vec4f xform(const Mat4f& m, const Vec4f& v)\n{ return v * m; }\ninline Mat4f xform(const Mat4f& m, const Mat4f& n)\n{ return n * m; }\n\n#else\ninline Mat2f Rot2f(float theta)                            { return CRot2f(theta); }\n\ninline Mat3f Rot3f(const Vec3f& axis, float theta)         { return CRot3f(axis, theta); }\ninline Mat3f Rot3f(const Vec4f& quaternion)               { return CRot3f(quaternion); }\ninline Mat3f Rot3f(const Vec3f& from, const Vec3f& to)    { return CRot3f(from, to); }\n\ninline Mat3f HRot3f  (float theta)                         { return HCRot3f(theta); }\ninline Mat3f HTrans3f(const Vec2f& t)                     { return HCTrans3f(t); }\n\ninline Mat4f HRot4f  (const Vec3f& axis, float theta)      { return HCRot4f(axis, theta); }\ninline Mat4f HRot4f  (const Vec4f& q)                     { return HCRot4f(q); }\ninline Mat4f HRot4f  (const Vec3f& from, const Vec3f& to) { return HCRot4f(from, to); }\ninline Mat4f HTrans4f(const Vec3f& t)                     { return HCTrans4f(t); }\n\ninline Vec2f xform(const Mat2f& m, const Vec2f& v)\n{ return m * v; }\ninline Mat2f xform(const Mat2f& m, const Mat2f& n)\n{ return m * n; }\n\ninline Vec2f xform(const Mat3f& m, const Vec2f& v)\n{ return proj(m * Vec3f(v, float(1))); }\ninline Vec3f xform(const Mat3f& m, const Vec3f& v)\n{ return m * v; }\ninline Mat3f xform(const Mat3f& m, const Mat3f& n)\n{ return m * n; }\n\ninline Vec3f xform(const Mat4f& m, const Vec3f& v)\n{ return proj(m * Vec4f(v, float(1))); }\ninline Vec4f xform(const Mat4f& m, const Vec4f& v)\n{ return m * v; }\ninline Mat4f xform(const Mat4f& m, const Mat4f& n)\n{ return m * n; }\n#endif\n\n\n#endif\n\n#include <iostream>\n\n#ifndef VL_NO_IOSTREAM\n\n#ifndef VLF_STREAM_234_H\n#define VLF_STREAM_234_H\n\n\n\n\n// --- Stream Operators -------------------------------------------------------\n\nclass Vec2f;\nclass Vec3f;\nclass Vec4f;\nclass Mat2f;\nclass Mat3f;\nclass Mat4f;\n\nstd::ostream& operator << (std::ostream& s, const Vec2f& v);\nstd::istream& operator >> (std::istream& s, Vec2f& v);\nstd::ostream& operator << (std::ostream& s, const Vec3f& v);\nstd::istream& operator >> (std::istream& s, Vec3f& v);\nstd::ostream& operator << (std::ostream& s, const Vec4f& v);\nstd::istream& operator >> (std::istream& s, Vec4f& v);\n\nstd::ostream& operator << (std::ostream& s, const Mat2f& m);\nstd::istream& operator >> (std::istream& s, Mat2f& m);\nstd::ostream& operator << (std::ostream& s, const Mat3f& m);\nstd::istream& operator >> (std::istream& s, Mat3f& m);\nstd::ostream& operator << (std::ostream& s, const Mat4f& m);\nstd::istream& operator >> (std::istream& s, Mat4f& m);\n\n#endif\n#endif\n#endif\n"
  },
  {
    "path": "gen-images.sh",
    "content": "\n\nmkdir -p images\n\nSUNSKY=\"./sunsky -f -v -l 30 0 -d 58 -b 3\"\nOPTS=\n\nTIMES=\"12.5 13.5 14.5 15.5 16.5 17.5 18.5\"\nROUGHNESSES=\"0 0.166 0.333 0.5 0.666 0.833 1\"\n\nfor type in hosek preetham; do\n    echo $type \":\"\n    i=1\n    for time in $TIMES; do\n        $SUNSKY $OPTS -s $type -t $time && mv -f sky-hemi.png images/${type}-${i}.png\n        let \"i++\"\n    done\ndone\n\nOPTS=\"-o 0.5\"\n\nfor type in hosek preetham; do\n    echo $type \":\"\n    i=1\n    for time in $TIMES; do\n        $SUNSKY $OPTS -s $type -t $time && mv -f sky-hemi.png images/${type}-oc-${i}.png\n        let \"i++\"\n    done\ndone\n\nOPTS=\"\"\n\nfor type in hosek preetham; do\n    echo $type \":\"\n    i=1\n    for o in $ROUGHNESSES; do\n        $SUNSKY $OPTS -s $type -t 14 -o $o && mv -f sky-hemi.png images/${type}-ocd-${i}.png\n        # $SUNSKY $OPTS -s $type -t 07 -o $o && mv -f sky-hemi.png images/${type}-ocs-${i}.png\n        let \"i++\"\n    done\ndone\n\nOPTS=\"\"\n\nfor type in hosekBRDF preethamBRDF; do\n    echo $type \":\"\n    i=1\n    for r in $ROUGHNESSES; do\n        $SUNSKY $OPTS -s $type -t 14 -r $r && mv -f sky-hemi.png images/${type}-rd-${i}.png\n        $SUNSKY $OPTS -s $type -t 07 -r $r && mv -f sky-hemi.png images/${type}-rs-${i}.png\n        let \"i++\"\n    done\ndone\n\nmogrify -resize 120x120 images/*\n"
  },
  {
    "path": "sky.sh",
    "content": "//\n// Functions for evaluating sky model for sky box quad and lighting\n//\n// See https://github.com/andrewwillmott/sun-sky\n//\n\n#ifndef SKY_LIB_SH\n#define SKY_LIB_SH\n\nuniform vec4 u_skyInfo[3];\n// C++ bind code:\n//    const SkyHosek& hk = ...;\n//\n//    Vec3f cH(hk.mCoeffsXYZ[0][7], hk.mCoeffsXYZ[1][7], hk.mCoeffsXYZ[2][7]);\n//    Vec3f cI(hk.mCoeffsXYZ[0][2] - 1.0f, hk.mCoeffsXYZ[1][2] - 1.0f, hk.mCoeffsXYZ[2][2] - 1.0f);\n//    Vec3f rd(hk.mRadXYZ);\n//\n//    Vec4f skyInfo[3] =\n//    {\n//        { cH, lumScale },\n//        { cI, mRoughness },\n//        { rd, mParams.skyboxScale }\n//    };\n//\n//    SetUniform(\"skyInfo\", skyInfo);\n\n\nuniform sampler2D s_skyBRDFTable;\n// C++ bind code:\n//    SkyHosek& skyHosek = ...;\n//    SkyTable& skyTable = ...;\n//    SkyBRDF&  skyBRDF  = ...;\n//\n//    skyHosek.Update(sunDir, turbidity, groundAlbedo, overcast);\n//    skyTable.FindThetaGammaTables(skyHosek);\n//    skyBRDF.FindBRDFTables(skyTable, skyHosek);\n//\n//    int width  =     SkyBRDF::kTableSize;\n//    int height = 4 * SkyBRDF::kBRDFSamples;\n//\n//    imageData.resize(sizeof(Vec4f) * width * height);\n//    float* f = imageData.data();\n//    skyBRDF.FillBRDFTexture(width, height, *(float(*) [][4]) f);\n//    UpdateTexture(\"skyBRDFTable\", imageData.size(), imageData.data());\n//\n\nvec3 XYZToRGB(vec3 c)\n{\n    const vec3 XYZ_TO_R = vec3( 3.2404542, -1.5371385, -0.4985314);\n    const vec3 XYZ_TO_G = vec3(-0.9692660,  1.8760108,  0.0415560);\n    const vec3 XYZ_TO_B = vec3( 0.0556434, -0.2040259,  1.0572252);\n\n    return vec3\n    (\n        dot(XYZ_TO_R, c),\n        dot(XYZ_TO_G, c),\n        dot(XYZ_TO_B, c)\n    );\n}\n\nvec3 skyColourTable(float cosTheta, float cosGamma, float r)\n{\n    // remap roughness to table rows\n    float row_t  = (0.5 + 7.0 * r) / 8.0 / 4.0;\n    float row_g  = row_t + 0.25;\n    float row_fh = row_t + 0.50;\n\n    cosTheta = cosTheta >= 0.0f ? sqrt(cosTheta) : -sqrt(-cosTheta);        // remap to concentrate around horizon (0): REMAP_THETA\n    float t = 0.5 * (cosTheta + 1.0);\n\n    float g = 0.5 * (1.0 - cosGamma);  // remap to concentrate around sun\n    g = sqrt(g);\n\n    vec3 F = texture2D(s_skyBRDFTable, vec2(t, row_t)).xyz;\n    vec3 G = texture2D(s_skyBRDFTable, vec2(g, row_g)).xyz;\n\n    vec3 cH = u_skyInfo[0].xyz;\n    vec3 cI = u_skyInfo[1].xyz;\n\n    vec4 FHandH = texture2D(s_skyBRDFTable, vec2(t, row_fh));\n    vec3 H  = cH * vec3_splat(FHandH.w);\n    vec3 FH = cH * FHandH.xyz;\n\n    H  +=     cI;\n    FH += F * cI;\n\n    // (1 - F(theta)) * (1 + G(phi) + H(theta))\n    vec3 XYZ = (vec3_splat(1.0) - F) * (vec3_splat(1.0) + G) + H - FH;\n\n    XYZ = max(XYZ, vec3_splat(0.0));\n    XYZ *= u_skyInfo[2].xyz;\n\n    float lumScale = u_skyInfo[0].w;\n\n    return XYZToRGB(XYZ * lumScale);\n}\n\nvec3 skyColourTable(vec3 viewDir, vec3 sunDir)\n{\n    float cosTheta = viewDir.z;\n    float cosGamma = dot(viewDir, sunDir);\n\n    return skyColourTable(cosTheta, cosGamma, 0.0);\n}\n\nvec3 skyColourTable(vec3 viewDir, vec3 sunDir, float roughness)\n{\n    float cosTheta = viewDir.z;\n    float cosGamma = dot(viewDir, sunDir);\n\n    return skyColourTable(cosTheta, cosGamma, roughness);\n}\n\n#endif\n"
  },
  {
    "path": "skybox_fs.sc",
    "content": "//\n// Sky quad shader\n//\n\n$input v_param0\n\n#include \"lib/sky.sh\"\n\nuniform vec4 u_sunColour;\nuniform vec4 u_sunDirection;\nuniform vec4 u_groundColour;\n\nvoid main()\n{\n    vec3 dir = normalize(v_param0.xyz);  // world space direction of ray through pixel\n\n    vec3 skyColour = skyColourTable(dir, u_sunDirection.xyz);\n    vec3 sunColour = u_sunColour.xyz;\n\n    float overcast = u_sunColour.w;\n    float ss = overcast * overcast;\n\n    // Apply sun\n    float cosSA = 0.999957;  // cos(sunAngle) = cos(0.5332)\n    float cosAA = 1e-5;  // tiny anti-aliasing factor\n    float cosSA1 = cosSA + cosAA;  // outer rim\n    float cosSA0 = cosSA - 0.01 * saturate(ss * ss - 0.125);  // inner rim -- smaller as cloud cover increases\n\n    float cosSun = dot(dir, u_sunDirection.xyz);\n\n    // smooth out sun term as it gets overcast\n    float sunWeight = smoothstep(cosSA0 - 2e-5, cosSA1 + 2e-5f, cosSun);\n\n    vec3 colour = skyColour + sunColour * sunWeight;\n\n    gl_FragColor.xyz = toneMap(colour);\n    gl_FragColor.a = 1.0;\n}\n"
  },
  {
    "path": "stb_image_mini.h",
    "content": "// This is a cutdown version of stb_image[_write].h supporting only png and jpeg.\n// See the original for documentation and full licenses at the URLs below.\n// (License is MIT or public domain.)\n// Generated via:\n//   unifdef -USTBI_NO_JPEG -USTBI_NEON -USTBI_SSE2 -DSTBI_NO_BMP -DSTBI_NO_PSD -DSTBI_NO_TGA -DSTBI_NO_GIF -DSTBI_NO_HDR -DSTBI_NO_PIC -DSTBI_NO_PNM  -DSTBI_NO_LINEAR -DSTBI_NO_SIMD -USTBI_NEON -USTBI_NO_STDIO -USTBI_NO_PNG -USTB_IMAGE_STATIC  stb_image.h > stb_image_mini.h\n// + stb_image_write.h\n// - 16-bit API\n// - STBI_ONLY_XXX block\n// - unused functions\n// - doc comments\n// + STB_IMAGE_DECLARATION if you only want the declarations\n//\n// stb_image - v2.19 - public domain image loader - http://nothings.org/stb/stb_image.h\n//                                  no warranty implied; use at your own risk\n// stb_image_write - v0.98 - public domain - http://nothings.org/stb/stb_image_write.h\n// writes out PNG/BMP/TGA images to C stdio - Sean Barrett 2010\n//                          no warranty implied; use at your own risk\n//\n\n#ifndef STBI_INCLUDE_STB_IMAGE_H\n#define STBI_INCLUDE_STB_IMAGE_H\n\n#include <stdio.h>\n\n#define STBI_VERSION 1\n\nenum\n{\n   STBI_default = 0, // only used for desired_channels\n\n   STBI_grey       = 1,\n   STBI_grey_alpha = 2,\n   STBI_rgb        = 3,\n   STBI_rgb_alpha  = 4\n};\n\ntypedef unsigned char stbi_uc;\ntypedef unsigned short stbi_us;\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#define STBIDEF extern\n\n// load image by filename, open file, or memory buffer\n\ntypedef struct\n{\n   int      (*read)  (void *user,char *data,int size);   // fill 'data' with 'size' bytes.  return number of bytes actually read\n   void     (*skip)  (void *user,int n);                 // skip the next 'n' bytes, or 'unget' the last -n bytes if negative\n   int      (*eof)   (void *user);                       // returns nonzero if we are at end of file/data\n} stbi_io_callbacks;\n\n// 8-bits-per-channel interface\n\nSTBIDEF stbi_uc *stbi_load_from_memory   (stbi_uc           const *buffer, int len   , int *x, int *y, int *channels_in_file, int desired_channels);\nSTBIDEF stbi_uc *stbi_load_from_callbacks(stbi_io_callbacks const *clbk  , void *user, int *x, int *y, int *channels_in_file, int desired_channels);\n\n\nSTBIDEF stbi_uc *stbi_load            (char const *filename, int *x, int *y, int *channels_in_file, int desired_channels);\nSTBIDEF stbi_uc *stbi_load_from_file  (FILE *f, int *x, int *y, int *channels_in_file, int desired_channels);\n// for stbi_load_from_file, file pointer is left pointing immediately after image\n\n// get a VERY brief reason for failure\n// NOT THREADSAFE\nSTBIDEF const char *stbi_failure_reason  (void);\n\n// free the loaded image -- this is just free()\nSTBIDEF void     stbi_image_free      (void *retval_from_stbi_load);\n\n// get image dimensions & components without fully decoding\nSTBIDEF int      stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp);\nSTBIDEF int      stbi_info_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp);\n\nSTBIDEF int      stbi_info               (char const *filename,     int *x, int *y, int *comp);\nSTBIDEF int      stbi_info_from_file     (FILE *f,                  int *x, int *y, int *comp);\n\n\n\n// for image formats that explicitly notate that they have premultiplied alpha,\n// we just return the colors as stored in the file. set this flag to force\n// unpremultiplication. results are undefined if the unpremultiply overflow.\nSTBIDEF void stbi_set_unpremultiply_on_load(int flag_true_if_should_unpremultiply);\n\n// indicate whether we should process iphone images back to canonical format,\n// or just pass them through \"as-is\"\nSTBIDEF void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert);\n\n// flip the image vertically, so the first pixel in the output array is the bottom left\nSTBIDEF void stbi_set_flip_vertically_on_load(int flag_true_if_should_flip);\n\n// ZLIB client - used by PNG, available for other purposes\n\nSTBIDEF char *stbi_zlib_decode_malloc_guesssize(const char *buffer, int len, int initial_size, int *outlen);\nSTBIDEF char *stbi_zlib_decode_malloc_guesssize_headerflag(const char *buffer, int len, int initial_size, int *outlen, int parse_header);\nSTBIDEF char *stbi_zlib_decode_malloc(const char *buffer, int len, int *outlen);\nSTBIDEF int   stbi_zlib_decode_buffer(char *obuffer, int olen, const char *ibuffer, int ilen);\n\nSTBIDEF char *stbi_zlib_decode_noheader_malloc(const char *buffer, int len, int *outlen);\nSTBIDEF int   stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const char *ibuffer, int ilen);\n\n\n// stb_image_write interface\nSTBIDEF int stbi_write_png(char const *filename, int w, int h, int comp, const void  *data, int stride_in_bytes);\nSTBIDEF int stbi_write_bmp(char const *filename, int w, int h, int comp, const void  *data);\nSTBIDEF int stbi_write_tga(char const *filename, int w, int h, int comp, const void  *data);\n\nSTBIDEF stbi_uc *stbi_write_png_to_mem(stbi_uc *pixels, int stride_bytes, int x, int y, int n, int *out_len);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n\n\n#ifndef STB_IMAGE_DECLARATION\n\n#include <stdarg.h>\n#include <stddef.h> // ptrdiff_t on osx\n#include <stdlib.h>\n#include <string.h>\n#include <limits.h>\n\n\n#include <stdio.h>\n\n#ifndef STBI_ASSERT\n#include <assert.h>\n#define STBI_ASSERT(x) assert(x)\n#endif\n\n\n#ifndef _MSC_VER\n   #ifdef __cplusplus\n   #define stbi_inline inline\n   #else\n   #define stbi_inline\n   #endif\n#else\n   #define stbi_inline __forceinline\n#endif\n\n\n#ifdef _MSC_VER\ntypedef unsigned short stbi__uint16;\ntypedef   signed short stbi__int16;\ntypedef unsigned int   stbi__uint32;\ntypedef   signed int   stbi__int32;\n#else\n#include <stdint.h>\ntypedef uint16_t stbi__uint16;\ntypedef int16_t  stbi__int16;\ntypedef uint32_t stbi__uint32;\ntypedef int32_t  stbi__int32;\n#endif\n\n// should produce compiler error if size is wrong\ntypedef unsigned char validate_uint32[sizeof(stbi__uint32)==4 ? 1 : -1];\n\n#ifdef _MSC_VER\n#define STBI_NOTUSED(v)  (void)(v)\n#else\n#define STBI_NOTUSED(v)  (void)sizeof(v)\n#endif\n\n#ifdef _MSC_VER\n#define STBI_HAS_LROTL\n#endif\n\n#ifdef STBI_HAS_LROTL\n   #define stbi_lrot(x,y)  _lrotl(x,y)\n#else\n   #define stbi_lrot(x,y)  (((x) << (y)) | ((x) >> (32 - (y))))\n#endif\n\n#if defined(STBI_MALLOC) && defined(STBI_FREE) && (defined(STBI_REALLOC) || defined(STBI_REALLOC_SIZED))\n// ok\n#elif !defined(STBI_MALLOC) && !defined(STBI_FREE) && !defined(STBI_REALLOC) && !defined(STBI_REALLOC_SIZED)\n// ok\n#else\n#error \"Must define all or none of STBI_MALLOC, STBI_FREE, and STBI_REALLOC (or STBI_REALLOC_SIZED).\"\n#endif\n\n#ifndef STBI_MALLOC\n#define STBI_MALLOC(sz)           malloc(sz)\n#define STBI_REALLOC(p,newsz)     realloc(p,newsz)\n#define STBI_FREE(p)              free(p)\n#endif\n\n#ifndef STBI_REALLOC_SIZED\n#define STBI_REALLOC_SIZED(p,oldsz,newsz) STBI_REALLOC(p,newsz)\n#endif\n\n// x86/x64 detection\n#if defined(__x86_64__) || defined(_M_X64)\n#define STBI__X64_TARGET\n#elif defined(__i386) || defined(_M_IX86)\n#define STBI__X86_TARGET\n#endif\n\n\n\n\n// ARM NEON\n\n\n#ifndef STBI_SIMD_ALIGN\n#define STBI_SIMD_ALIGN(type, name) type name\n#endif\n\n///////////////////////////////////////////////\n//\n//  stbi__context struct and start_xxx functions\n\n// stbi__context structure is our basic context used by all images, so it\n// contains all the IO context, plus some basic image information\ntypedef struct\n{\n   stbi__uint32 img_x, img_y;\n   int img_n, img_out_n;\n\n   stbi_io_callbacks io;\n   void *io_user_data;\n\n   int read_from_callbacks;\n   int buflen;\n   stbi_uc buffer_start[128];\n\n   stbi_uc *img_buffer, *img_buffer_end;\n   stbi_uc *img_buffer_original, *img_buffer_original_end;\n} stbi__context;\n\n\nstatic void stbi__refill_buffer(stbi__context *s);\n\n// initialize a memory-decode context\nstatic void stbi__start_mem(stbi__context *s, stbi_uc const *buffer, int len)\n{\n   s->io.read = NULL;\n   s->read_from_callbacks = 0;\n   s->img_buffer = s->img_buffer_original = (stbi_uc *) buffer;\n   s->img_buffer_end = s->img_buffer_original_end = (stbi_uc *) buffer+len;\n}\n\n// initialize a callback-based context\nstatic void stbi__start_callbacks(stbi__context *s, stbi_io_callbacks *c, void *user)\n{\n   s->io = *c;\n   s->io_user_data = user;\n   s->buflen = sizeof(s->buffer_start);\n   s->read_from_callbacks = 1;\n   s->img_buffer_original = s->buffer_start;\n   stbi__refill_buffer(s);\n   s->img_buffer_original_end = s->img_buffer_end;\n}\n\n\nstatic int stbi__stdio_read(void *user, char *data, int size)\n{\n   return (int) fread(data,1,size,(FILE*) user);\n}\n\nstatic void stbi__stdio_skip(void *user, int n)\n{\n   fseek((FILE*) user, n, SEEK_CUR);\n}\n\nstatic int stbi__stdio_eof(void *user)\n{\n   return feof((FILE*) user);\n}\n\nstatic stbi_io_callbacks stbi__stdio_callbacks =\n{\n   stbi__stdio_read,\n   stbi__stdio_skip,\n   stbi__stdio_eof,\n};\n\nstatic void stbi__start_file(stbi__context *s, FILE *f)\n{\n   stbi__start_callbacks(s, &stbi__stdio_callbacks, (void *) f);\n}\n\n//static void stop_file(stbi__context *s) { }\n\n\nstatic void stbi__rewind(stbi__context *s)\n{\n   // conceptually rewind SHOULD rewind to the beginning of the stream,\n   // but we just rewind to the beginning of the initial buffer, because\n   // we only use it after doing 'test', which only ever looks at at most 92 bytes\n   s->img_buffer = s->img_buffer_original;\n   s->img_buffer_end = s->img_buffer_original_end;\n}\n\nenum\n{\n   STBI_ORDER_RGB,\n   STBI_ORDER_BGR\n};\n\ntypedef struct\n{\n   int bits_per_channel;\n   int num_channels;\n   int channel_order;\n} stbi__result_info;\n\nstatic int      stbi__jpeg_test(stbi__context *s);\nstatic void    *stbi__jpeg_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri);\nstatic int      stbi__jpeg_info(stbi__context *s, int *x, int *y, int *comp);\n\nstatic int      stbi__png_test(stbi__context *s);\nstatic void    *stbi__png_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri);\nstatic int      stbi__png_info(stbi__context *s, int *x, int *y, int *comp);\n\n\n\n\n\n\n\n\n// this is not threadsafe\nstatic const char *stbi__g_failure_reason;\n\nSTBIDEF const char *stbi_failure_reason(void)\n{\n   return stbi__g_failure_reason;\n}\n\nstatic int stbi__err(const char *str)\n{\n   stbi__g_failure_reason = str;\n   return 0;\n}\n\nstatic void *stbi__malloc(size_t size)\n{\n    return STBI_MALLOC(size);\n}\n\n// stb_image uses ints pervasively, including for offset calculations.\n// therefore the largest decoded image size we can support with the\n// current code, even on 64-bit targets, is INT_MAX. this is not a\n// significant limitation for the intended use case.\n//\n// we do, however, need to make sure our size calculations don't\n// overflow. hence a few helper functions for size calculations that\n// multiply integers together, making sure that they're non-negative\n// and no overflow occurs.\n\n// return 1 if the sum is valid, 0 on overflow.\n// negative terms are considered invalid.\nstatic int stbi__addsizes_valid(int a, int b)\n{\n   if (b < 0) return 0;\n   // now 0 <= b <= INT_MAX, hence also\n   // 0 <= INT_MAX - b <= INTMAX.\n   // And \"a + b <= INT_MAX\" (which might overflow) is the\n   // same as a <= INT_MAX - b (no overflow)\n   return a <= INT_MAX - b;\n}\n\n// returns 1 if the product is valid, 0 on overflow.\n// negative factors are considered invalid.\nstatic int stbi__mul2sizes_valid(int a, int b)\n{\n   if (a < 0 || b < 0) return 0;\n   if (b == 0) return 1; // mul-by-0 is always safe\n   // portable way to check for no overflows in a*b\n   return a <= INT_MAX/b;\n}\n\n// returns 1 if \"a*b + add\" has no negative terms/factors and doesn't overflow\nstatic int stbi__mad2sizes_valid(int a, int b, int add)\n{\n   return stbi__mul2sizes_valid(a, b) && stbi__addsizes_valid(a*b, add);\n}\n\n// returns 1 if \"a*b*c + add\" has no negative terms/factors and doesn't overflow\nstatic int stbi__mad3sizes_valid(int a, int b, int c, int add)\n{\n   return stbi__mul2sizes_valid(a, b) && stbi__mul2sizes_valid(a*b, c) &&\n      stbi__addsizes_valid(a*b*c, add);\n}\n\n// returns 1 if \"a*b*c*d + add\" has no negative terms/factors and doesn't overflow\n\n// mallocs with size overflow checking\nstatic void *stbi__malloc_mad2(int a, int b, int add)\n{\n   if (!stbi__mad2sizes_valid(a, b, add)) return NULL;\n   return stbi__malloc(a*b + add);\n}\n\nstatic void *stbi__malloc_mad3(int a, int b, int c, int add)\n{\n   if (!stbi__mad3sizes_valid(a, b, c, add)) return NULL;\n   return stbi__malloc(a*b*c + add);\n}\n\n\n// stbi__err - error\n// stbi__errpf - error returning pointer to float\n// stbi__errpuc - error returning pointer to unsigned char\n\n#ifdef STBI_NO_FAILURE_STRINGS\n   #define stbi__err(x,y)  0\n#elif defined(STBI_FAILURE_USERMSG)\n   #define stbi__err(x,y)  stbi__err(y)\n#else\n   #define stbi__err(x,y)  stbi__err(x)\n#endif\n\n#define stbi__errpf(x,y)   ((float *)(size_t) (stbi__err(x,y)?NULL:NULL))\n#define stbi__errpuc(x,y)  ((unsigned char *)(size_t) (stbi__err(x,y)?NULL:NULL))\n\nSTBIDEF void stbi_image_free(void *retval_from_stbi_load)\n{\n   STBI_FREE(retval_from_stbi_load);\n}\n\n\n\nstatic int stbi__vertically_flip_on_load = 0;\n\nSTBIDEF void stbi_set_flip_vertically_on_load(int flag_true_if_should_flip)\n{\n    stbi__vertically_flip_on_load = flag_true_if_should_flip;\n}\n\nstatic void *stbi__load_main(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri, int )\n{\n   memset(ri, 0, sizeof(*ri)); // make sure it's initialized if we add new fields\n   ri->bits_per_channel = 8; // default is 8 so most paths don't have to be changed\n   ri->channel_order = STBI_ORDER_RGB; // all current input & output are this, but this is here so we can add BGR order\n   ri->num_channels = 0;\n\n   if (stbi__jpeg_test(s)) return stbi__jpeg_load(s,x,y,comp,req_comp, ri);\n   if (stbi__png_test(s))  return stbi__png_load(s,x,y,comp,req_comp, ri);\n\n\n\n   return stbi__errpuc(\"unknown image type\", \"Image not of any known type, or corrupt\");\n}\n\nstatic void stbi__vertical_flip(void *image, int w, int h, int bytes_per_pixel)\n{\n   int row;\n   size_t bytes_per_row = (size_t)w * bytes_per_pixel;\n   stbi_uc temp[2048];\n   stbi_uc *bytes = (stbi_uc *)image;\n\n   for (row = 0; row < (h>>1); row++) {\n      stbi_uc *row0 = bytes + row*bytes_per_row;\n      stbi_uc *row1 = bytes + (h - row - 1)*bytes_per_row;\n      // swap row0 with row1\n      size_t bytes_left = bytes_per_row;\n      while (bytes_left) {\n         size_t bytes_copy = (bytes_left < sizeof(temp)) ? bytes_left : sizeof(temp);\n         memcpy(temp, row0, bytes_copy);\n         memcpy(row0, row1, bytes_copy);\n         memcpy(row1, temp, bytes_copy);\n         row0 += bytes_copy;\n         row1 += bytes_copy;\n         bytes_left -= bytes_copy;\n      }\n   }\n}\n\nstatic unsigned char *stbi__load_and_postprocess_8bit(stbi__context *s, int *x, int *y, int *comp, int req_comp)\n{\n   stbi__result_info ri;\n   void *result = stbi__load_main(s, x, y, comp, req_comp, &ri, 8);\n\n   if (result == NULL)\n      return NULL;\n\n   // @TODO: move stbi__convert_format to here\n\n   if (stbi__vertically_flip_on_load) {\n      int channels = req_comp ? req_comp : *comp;\n      stbi__vertical_flip(result, *x, *y, channels * sizeof(stbi_uc));\n   }\n\n   return (unsigned char *) result;\n}\n\nstatic FILE *stbi__fopen(char const *filename, char const *mode)\n{\n   FILE *f;\n#if defined(_MSC_VER) && _MSC_VER >= 1400\n   if (0 != fopen_s(&f, filename, mode))\n      f=0;\n#else\n   f = fopen(filename, mode);\n#endif\n   return f;\n}\n\n\nSTBIDEF stbi_uc *stbi_load(char const *filename, int *x, int *y, int *comp, int req_comp)\n{\n   FILE *f = stbi__fopen(filename, \"rb\");\n   unsigned char *result;\n   if (!f) return stbi__errpuc(\"can't fopen\", \"Unable to open file\");\n   result = stbi_load_from_file(f,x,y,comp,req_comp);\n   fclose(f);\n   return result;\n}\n\nSTBIDEF stbi_uc *stbi_load_from_file(FILE *f, int *x, int *y, int *comp, int req_comp)\n{\n   unsigned char *result;\n   stbi__context s;\n   stbi__start_file(&s,f);\n   result = stbi__load_and_postprocess_8bit(&s,x,y,comp,req_comp);\n   if (result) {\n      // need to 'unget' all the characters in the IO buffer\n      fseek(f, - (int) (s.img_buffer_end - s.img_buffer), SEEK_CUR);\n   }\n   return result;\n}\n\nSTBIDEF stbi_uc *stbi_load_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp)\n{\n   stbi__context s;\n   stbi__start_mem(&s,buffer,len);\n   return stbi__load_and_postprocess_8bit(&s,x,y,comp,req_comp);\n}\n\nSTBIDEF stbi_uc *stbi_load_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp)\n{\n   stbi__context s;\n   stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user);\n   return stbi__load_and_postprocess_8bit(&s,x,y,comp,req_comp);\n}\n\n\n//////////////////////////////////////////////////////////////////////////////\n//\n// Common code used by all image loaders\n//\n\nenum\n{\n   STBI__SCAN_load=0,\n   STBI__SCAN_type,\n   STBI__SCAN_header\n};\n\nstatic void stbi__refill_buffer(stbi__context *s)\n{\n   int n = (s->io.read)(s->io_user_data,(char*)s->buffer_start,s->buflen);\n   if (n == 0) {\n      // at end of file, treat same as if from memory, but need to handle case\n      // where s->img_buffer isn't pointing to safe memory, e.g. 0-byte file\n      s->read_from_callbacks = 0;\n      s->img_buffer = s->buffer_start;\n      s->img_buffer_end = s->buffer_start+1;\n      *s->img_buffer = 0;\n   } else {\n      s->img_buffer = s->buffer_start;\n      s->img_buffer_end = s->buffer_start + n;\n   }\n}\n\nstbi_inline static stbi_uc stbi__get8(stbi__context *s)\n{\n   if (s->img_buffer < s->img_buffer_end)\n      return *s->img_buffer++;\n   if (s->read_from_callbacks) {\n      stbi__refill_buffer(s);\n      return *s->img_buffer++;\n   }\n   return 0;\n}\n\nstbi_inline static int stbi__at_eof(stbi__context *s)\n{\n   if (s->io.read) {\n      if (!(s->io.eof)(s->io_user_data)) return 0;\n      // if feof() is true, check if buffer = end\n      // special case: we've only got the special 0 character at the end\n      if (s->read_from_callbacks == 0) return 1;\n   }\n\n   return s->img_buffer >= s->img_buffer_end;\n}\n\nstatic void stbi__skip(stbi__context *s, int n)\n{\n   if (n < 0) {\n      s->img_buffer = s->img_buffer_end;\n      return;\n   }\n   if (s->io.read) {\n      int blen = (int) (s->img_buffer_end - s->img_buffer);\n      if (blen < n) {\n         s->img_buffer = s->img_buffer_end;\n         (s->io.skip)(s->io_user_data, n - blen);\n         return;\n      }\n   }\n   s->img_buffer += n;\n}\n\nstatic int stbi__getn(stbi__context *s, stbi_uc *buffer, int n)\n{\n   if (s->io.read) {\n      int blen = (int) (s->img_buffer_end - s->img_buffer);\n      if (blen < n) {\n         int res, count;\n\n         memcpy(buffer, s->img_buffer, blen);\n\n         count = (s->io.read)(s->io_user_data, (char*) buffer + blen, n - blen);\n         res = (count == (n-blen));\n         s->img_buffer = s->img_buffer_end;\n         return res;\n      }\n   }\n\n   if (s->img_buffer+n <= s->img_buffer_end) {\n      memcpy(buffer, s->img_buffer, n);\n      s->img_buffer += n;\n      return 1;\n   } else\n      return 0;\n}\n\nstatic int stbi__get16be(stbi__context *s)\n{\n   int z = stbi__get8(s);\n   return (z << 8) + stbi__get8(s);\n}\n\nstatic stbi__uint32 stbi__get32be(stbi__context *s)\n{\n   stbi__uint32 z = stbi__get16be(s);\n   return (z << 16) + stbi__get16be(s);\n}\n\n// nothing\n\n\n#define STBI__BYTECAST(x)  ((stbi_uc) ((x) & 255))  // truncate int to byte without warnings\n\n\n//////////////////////////////////////////////////////////////////////////////\n//\n//  generic converter from built-in img_n to req_comp\n//    individual types do this automatically as much as possible (e.g. jpeg\n//    does all cases internally since it needs to colorspace convert anyway,\n//    and it never has alpha, so very few cases ). png can automatically\n//    interleave an alpha=255 channel, but falls back to this for other cases\n//\n//  assume data buffer is malloced, so malloc a new one and free that one\n//  only failure mode is malloc failing\n\nstatic stbi_uc stbi__compute_y(int r, int g, int b)\n{\n   return (stbi_uc) (((r*77) + (g*150) +  (29*b)) >> 8);\n}\n\nstatic unsigned char *stbi__convert_format(unsigned char *data, int img_n, int req_comp, unsigned int x, unsigned int y)\n{\n   int i,j;\n   unsigned char *good;\n\n   if (req_comp == img_n) return data;\n   STBI_ASSERT(req_comp >= 1 && req_comp <= 4);\n\n   good = (unsigned char *) stbi__malloc_mad3(req_comp, x, y, 0);\n   if (good == NULL) {\n      STBI_FREE(data);\n      return stbi__errpuc(\"outofmem\", \"Out of memory\");\n   }\n\n   for (j=0; j < (int) y; ++j) {\n      unsigned char *src  = data + j * x * img_n   ;\n      unsigned char *dest = good + j * x * req_comp;\n\n      #define STBI__COMBO(a,b)  ((a)*8+(b))\n      #define STBI__CASE(a,b)   case STBI__COMBO(a,b): for(i=x-1; i >= 0; --i, src += a, dest += b)\n      // convert source image with img_n components to one with req_comp components;\n      // avoid switch per pixel, so use switch per scanline and massive macros\n      switch (STBI__COMBO(img_n, req_comp)) {\n         STBI__CASE(1,2) { dest[0]=src[0]; dest[1]=255;                                     } break;\n         STBI__CASE(1,3) { dest[0]=dest[1]=dest[2]=src[0];                                  } break;\n         STBI__CASE(1,4) { dest[0]=dest[1]=dest[2]=src[0]; dest[3]=255;                     } break;\n         STBI__CASE(2,1) { dest[0]=src[0];                                                  } break;\n         STBI__CASE(2,3) { dest[0]=dest[1]=dest[2]=src[0];                                  } break;\n         STBI__CASE(2,4) { dest[0]=dest[1]=dest[2]=src[0]; dest[3]=src[1];                  } break;\n         STBI__CASE(3,4) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2];dest[3]=255;        } break;\n         STBI__CASE(3,1) { dest[0]=stbi__compute_y(src[0],src[1],src[2]);                   } break;\n         STBI__CASE(3,2) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); dest[1] = 255;    } break;\n         STBI__CASE(4,1) { dest[0]=stbi__compute_y(src[0],src[1],src[2]);                   } break;\n         STBI__CASE(4,2) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); dest[1] = src[3]; } break;\n         STBI__CASE(4,3) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2];                    } break;\n         default: STBI_ASSERT(0);\n      }\n      #undef STBI__CASE\n   }\n\n   STBI_FREE(data);\n   return good;\n}\n\n//////////////////////////////////////////////////////////////////////////////\n//\n//  \"baseline\" JPEG/JFIF decoder\n//\n//    simple implementation\n//      - doesn't support delayed output of y-dimension\n//      - simple interface (only one output format: 8-bit interleaved RGB)\n//      - doesn't try to recover corrupt jpegs\n//      - doesn't allow partial loading, loading multiple at once\n//      - still fast on x86 (copying globals into locals doesn't help x86)\n//      - allocates lots of intermediate memory (full size of all components)\n//        - non-interleaved case requires this anyway\n//        - allows good upsampling (see next)\n//    high-quality\n//      - upsampled channels are bilinearly interpolated, even across blocks\n//      - quality integer IDCT derived from IJG's 'slow'\n//    performance\n//      - fast huffman; reasonable integer IDCT\n//      - some SIMD kernels for common paths on targets with SSE2/NEON\n//      - uses a lot of intermediate memory, could cache poorly\n\n\n// huffman decoding acceleration\n#define FAST_BITS   9  // larger handles more cases; smaller stomps less cache\n\ntypedef struct\n{\n   stbi_uc  fast[1 << FAST_BITS];\n   // weirdly, repacking this into AoS is a 10% speed loss, instead of a win\n   stbi__uint16 code[256];\n   stbi_uc  values[256];\n   stbi_uc  size[257];\n   unsigned int maxcode[18];\n   int    delta[17];   // old 'firstsymbol' - old 'firstcode'\n} stbi__huffman;\n\ntypedef struct\n{\n   stbi__context *s;\n   stbi__huffman huff_dc[4];\n   stbi__huffman huff_ac[4];\n   stbi__uint16 dequant[4][64];\n   stbi__int16 fast_ac[4][1 << FAST_BITS];\n\n// sizes for components, interleaved MCUs\n   int img_h_max, img_v_max;\n   int img_mcu_x, img_mcu_y;\n   int img_mcu_w, img_mcu_h;\n\n// definition of jpeg image component\n   struct\n   {\n      int id;\n      int h,v;\n      int tq;\n      int hd,ha;\n      int dc_pred;\n\n      int x,y,w2,h2;\n      stbi_uc *data;\n      void *raw_data, *raw_coeff;\n      stbi_uc *linebuf;\n      short   *coeff;   // progressive only\n      int      coeff_w, coeff_h; // number of 8x8 coefficient blocks\n   } img_comp[4];\n\n   stbi__uint32   code_buffer; // jpeg entropy-coded buffer\n   int            code_bits;   // number of valid bits\n   unsigned char  marker;      // marker seen while filling entropy buffer\n   int            nomore;      // flag if we saw a marker so must stop\n\n   int            progressive;\n   int            spec_start;\n   int            spec_end;\n   int            succ_high;\n   int            succ_low;\n   int            eob_run;\n   int            jfif;\n   int            app14_color_transform; // Adobe APP14 tag\n   int            rgb;\n\n   int scan_n, order[4];\n   int restart_interval, todo;\n\n// kernels\n   void (*idct_block_kernel)(stbi_uc *out, int out_stride, short data[64]);\n   void (*YCbCr_to_RGB_kernel)(stbi_uc *out, const stbi_uc *y, const stbi_uc *pcb, const stbi_uc *pcr, int count, int step);\n   stbi_uc *(*resample_row_hv_2_kernel)(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs);\n} stbi__jpeg;\n\nstatic int stbi__build_huffman(stbi__huffman *h, int *count)\n{\n   int i,j,k=0;\n   unsigned int code;\n   // build size list for each symbol (from JPEG spec)\n   for (i=0; i < 16; ++i)\n      for (j=0; j < count[i]; ++j)\n         h->size[k++] = (stbi_uc) (i+1);\n   h->size[k] = 0;\n\n   // compute actual symbols (from jpeg spec)\n   code = 0;\n   k = 0;\n   for(j=1; j <= 16; ++j) {\n      // compute delta to add to code to compute symbol id\n      h->delta[j] = k - code;\n      if (h->size[k] == j) {\n         while (h->size[k] == j)\n            h->code[k++] = (stbi__uint16) (code++);\n         if (code-1 >= (1u << j)) return stbi__err(\"bad code lengths\",\"Corrupt JPEG\");\n      }\n      // compute largest code + 1 for this size, preshifted as needed later\n      h->maxcode[j] = code << (16-j);\n      code <<= 1;\n   }\n   h->maxcode[j] = 0xffffffff;\n\n   // build non-spec acceleration table; 255 is flag for not-accelerated\n   memset(h->fast, 255, 1 << FAST_BITS);\n   for (i=0; i < k; ++i) {\n      int s = h->size[i];\n      if (s <= FAST_BITS) {\n         int c = h->code[i] << (FAST_BITS-s);\n         int m = 1 << (FAST_BITS-s);\n         for (j=0; j < m; ++j) {\n            h->fast[c+j] = (stbi_uc) i;\n         }\n      }\n   }\n   return 1;\n}\n\n// build a table that decodes both magnitude and value of small ACs in\n// one go.\nstatic void stbi__build_fast_ac(stbi__int16 *fast_ac, stbi__huffman *h)\n{\n   int i;\n   for (i=0; i < (1 << FAST_BITS); ++i) {\n      stbi_uc fast = h->fast[i];\n      fast_ac[i] = 0;\n      if (fast < 255) {\n         int rs = h->values[fast];\n         int run = (rs >> 4) & 15;\n         int magbits = rs & 15;\n         int len = h->size[fast];\n\n         if (magbits && len + magbits <= FAST_BITS) {\n            // magnitude code followed by receive_extend code\n            int k = ((i << len) & ((1 << FAST_BITS) - 1)) >> (FAST_BITS - magbits);\n            int m = 1 << (magbits - 1);\n            if (k < m) k += (~0U << magbits) + 1;\n            // if the result is small enough, we can fit it in fast_ac table\n            if (k >= -128 && k <= 127)\n               fast_ac[i] = (stbi__int16) ((k * 256) + (run * 16) + (len + magbits));\n         }\n      }\n   }\n}\n\nstatic void stbi__grow_buffer_unsafe(stbi__jpeg *j)\n{\n   do {\n      unsigned int b = j->nomore ? 0 : stbi__get8(j->s);\n      if (b == 0xff) {\n         int c = stbi__get8(j->s);\n         while (c == 0xff) c = stbi__get8(j->s); // consume fill bytes\n         if (c != 0) {\n            j->marker = (unsigned char) c;\n            j->nomore = 1;\n            return;\n         }\n      }\n      j->code_buffer |= b << (24 - j->code_bits);\n      j->code_bits += 8;\n   } while (j->code_bits <= 24);\n}\n\n// (1 << n) - 1\nstatic const stbi__uint32 stbi__bmask[17]={0,1,3,7,15,31,63,127,255,511,1023,2047,4095,8191,16383,32767,65535};\n\n// decode a jpeg huffman value from the bitstream\nstbi_inline static int stbi__jpeg_huff_decode(stbi__jpeg *j, stbi__huffman *h)\n{\n   unsigned int temp;\n   int c,k;\n\n   if (j->code_bits < 16) stbi__grow_buffer_unsafe(j);\n\n   // look at the top FAST_BITS and determine what symbol ID it is,\n   // if the code is <= FAST_BITS\n   c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1);\n   k = h->fast[c];\n   if (k < 255) {\n      int s = h->size[k];\n      if (s > j->code_bits)\n         return -1;\n      j->code_buffer <<= s;\n      j->code_bits -= s;\n      return h->values[k];\n   }\n\n   // naive test is to shift the code_buffer down so k bits are\n   // valid, then test against maxcode. To speed this up, we've\n   // preshifted maxcode left so that it has (16-k) 0s at the\n   // end; in other words, regardless of the number of bits, it\n   // wants to be compared against something shifted to have 16;\n   // that way we don't need to shift inside the loop.\n   temp = j->code_buffer >> 16;\n   for (k=FAST_BITS+1 ; ; ++k)\n      if (temp < h->maxcode[k])\n         break;\n   if (k == 17) {\n      // error! code not found\n      j->code_bits -= 16;\n      return -1;\n   }\n\n   if (k > j->code_bits)\n      return -1;\n\n   // convert the huffman code to the symbol id\n   c = ((j->code_buffer >> (32 - k)) & stbi__bmask[k]) + h->delta[k];\n   STBI_ASSERT((((j->code_buffer) >> (32 - h->size[c])) & stbi__bmask[h->size[c]]) == h->code[c]);\n\n   // convert the id to a symbol\n   j->code_bits -= k;\n   j->code_buffer <<= k;\n   return h->values[c];\n}\n\n// bias[n] = (-1<<n) + 1\nstatic const int stbi__jbias[16] = {0,-1,-3,-7,-15,-31,-63,-127,-255,-511,-1023,-2047,-4095,-8191,-16383,-32767};\n\n// combined JPEG 'receive' and JPEG 'extend', since baseline\n// always extends everything it receives.\nstbi_inline static int stbi__extend_receive(stbi__jpeg *j, int n)\n{\n   unsigned int k;\n   int sgn;\n   if (j->code_bits < n) stbi__grow_buffer_unsafe(j);\n\n   sgn = (stbi__int32)j->code_buffer >> 31; // sign bit is always in MSB\n   k = stbi_lrot(j->code_buffer, n);\n   STBI_ASSERT(n >= 0 && n < (int) (sizeof(stbi__bmask)/sizeof(*stbi__bmask)));\n   j->code_buffer = k & ~stbi__bmask[n];\n   k &= stbi__bmask[n];\n   j->code_bits -= n;\n   return k + (stbi__jbias[n] & ~sgn);\n}\n\n// get some unsigned bits\nstbi_inline static int stbi__jpeg_get_bits(stbi__jpeg *j, int n)\n{\n   unsigned int k;\n   if (j->code_bits < n) stbi__grow_buffer_unsafe(j);\n   k = stbi_lrot(j->code_buffer, n);\n   j->code_buffer = k & ~stbi__bmask[n];\n   k &= stbi__bmask[n];\n   j->code_bits -= n;\n   return k;\n}\n\nstbi_inline static int stbi__jpeg_get_bit(stbi__jpeg *j)\n{\n   unsigned int k;\n   if (j->code_bits < 1) stbi__grow_buffer_unsafe(j);\n   k = j->code_buffer;\n   j->code_buffer <<= 1;\n   --j->code_bits;\n   return k & 0x80000000;\n}\n\n// given a value that's at position X in the zigzag stream,\n// where does it appear in the 8x8 matrix coded as row-major?\nstatic const stbi_uc stbi__jpeg_dezigzag[64+15] =\n{\n    0,  1,  8, 16,  9,  2,  3, 10,\n   17, 24, 32, 25, 18, 11,  4,  5,\n   12, 19, 26, 33, 40, 48, 41, 34,\n   27, 20, 13,  6,  7, 14, 21, 28,\n   35, 42, 49, 56, 57, 50, 43, 36,\n   29, 22, 15, 23, 30, 37, 44, 51,\n   58, 59, 52, 45, 38, 31, 39, 46,\n   53, 60, 61, 54, 47, 55, 62, 63,\n   // let corrupt input sample past end\n   63, 63, 63, 63, 63, 63, 63, 63,\n   63, 63, 63, 63, 63, 63, 63\n};\n\n// decode one 64-entry block--\nstatic int stbi__jpeg_decode_block(stbi__jpeg *j, short data[64], stbi__huffman *hdc, stbi__huffman *hac, stbi__int16 *fac, int b, stbi__uint16 *dequant)\n{\n   int diff,dc,k;\n   int t;\n\n   if (j->code_bits < 16) stbi__grow_buffer_unsafe(j);\n   t = stbi__jpeg_huff_decode(j, hdc);\n   if (t < 0) return stbi__err(\"bad huffman code\",\"Corrupt JPEG\");\n\n   // 0 all the ac values now so we can do it 32-bits at a time\n   memset(data,0,64*sizeof(data[0]));\n\n   diff = t ? stbi__extend_receive(j, t) : 0;\n   dc = j->img_comp[b].dc_pred + diff;\n   j->img_comp[b].dc_pred = dc;\n   data[0] = (short) (dc * dequant[0]);\n\n   // decode AC components, see JPEG spec\n   k = 1;\n   do {\n      unsigned int zig;\n      int c,r,s;\n      if (j->code_bits < 16) stbi__grow_buffer_unsafe(j);\n      c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1);\n      r = fac[c];\n      if (r) { // fast-AC path\n         k += (r >> 4) & 15; // run\n         s = r & 15; // combined length\n         j->code_buffer <<= s;\n         j->code_bits -= s;\n         // decode into unzigzag'd location\n         zig = stbi__jpeg_dezigzag[k++];\n         data[zig] = (short) ((r >> 8) * dequant[zig]);\n      } else {\n         int rs = stbi__jpeg_huff_decode(j, hac);\n         if (rs < 0) return stbi__err(\"bad huffman code\",\"Corrupt JPEG\");\n         s = rs & 15;\n         r = rs >> 4;\n         if (s == 0) {\n            if (rs != 0xf0) break; // end block\n            k += 16;\n         } else {\n            k += r;\n            // decode into unzigzag'd location\n            zig = stbi__jpeg_dezigzag[k++];\n            data[zig] = (short) (stbi__extend_receive(j,s) * dequant[zig]);\n         }\n      }\n   } while (k < 64);\n   return 1;\n}\n\nstatic int stbi__jpeg_decode_block_prog_dc(stbi__jpeg *j, short data[64], stbi__huffman *hdc, int b)\n{\n   int diff,dc;\n   int t;\n   if (j->spec_end != 0) return stbi__err(\"can't merge dc and ac\", \"Corrupt JPEG\");\n\n   if (j->code_bits < 16) stbi__grow_buffer_unsafe(j);\n\n   if (j->succ_high == 0) {\n      // first scan for DC coefficient, must be first\n      memset(data,0,64*sizeof(data[0])); // 0 all the ac values now\n      t = stbi__jpeg_huff_decode(j, hdc);\n      diff = t ? stbi__extend_receive(j, t) : 0;\n\n      dc = j->img_comp[b].dc_pred + diff;\n      j->img_comp[b].dc_pred = dc;\n      data[0] = (short) (dc << j->succ_low);\n   } else {\n      // refinement scan for DC coefficient\n      if (stbi__jpeg_get_bit(j))\n         data[0] += (short) (1 << j->succ_low);\n   }\n   return 1;\n}\n\n// @OPTIMIZE: store non-zigzagged during the decode passes,\n// and only de-zigzag when dequantizing\nstatic int stbi__jpeg_decode_block_prog_ac(stbi__jpeg *j, short data[64], stbi__huffman *hac, stbi__int16 *fac)\n{\n   int k;\n   if (j->spec_start == 0) return stbi__err(\"can't merge dc and ac\", \"Corrupt JPEG\");\n\n   if (j->succ_high == 0) {\n      int shift = j->succ_low;\n\n      if (j->eob_run) {\n         --j->eob_run;\n         return 1;\n      }\n\n      k = j->spec_start;\n      do {\n         unsigned int zig;\n         int c,r,s;\n         if (j->code_bits < 16) stbi__grow_buffer_unsafe(j);\n         c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1);\n         r = fac[c];\n         if (r) { // fast-AC path\n            k += (r >> 4) & 15; // run\n            s = r & 15; // combined length\n            j->code_buffer <<= s;\n            j->code_bits -= s;\n            zig = stbi__jpeg_dezigzag[k++];\n            data[zig] = (short) ((r >> 8) << shift);\n         } else {\n            int rs = stbi__jpeg_huff_decode(j, hac);\n            if (rs < 0) return stbi__err(\"bad huffman code\",\"Corrupt JPEG\");\n            s = rs & 15;\n            r = rs >> 4;\n            if (s == 0) {\n               if (r < 15) {\n                  j->eob_run = (1 << r);\n                  if (r)\n                     j->eob_run += stbi__jpeg_get_bits(j, r);\n                  --j->eob_run;\n                  break;\n               }\n               k += 16;\n            } else {\n               k += r;\n               zig = stbi__jpeg_dezigzag[k++];\n               data[zig] = (short) (stbi__extend_receive(j,s) << shift);\n            }\n         }\n      } while (k <= j->spec_end);\n   } else {\n      // refinement scan for these AC coefficients\n\n      short bit = (short) (1 << j->succ_low);\n\n      if (j->eob_run) {\n         --j->eob_run;\n         for (k = j->spec_start; k <= j->spec_end; ++k) {\n            short *p = &data[stbi__jpeg_dezigzag[k]];\n            if (*p != 0)\n               if (stbi__jpeg_get_bit(j))\n                  if ((*p & bit)==0) {\n                     if (*p > 0)\n                        *p += bit;\n                     else\n                        *p -= bit;\n                  }\n         }\n      } else {\n         k = j->spec_start;\n         do {\n            int r,s;\n            int rs = stbi__jpeg_huff_decode(j, hac); // @OPTIMIZE see if we can use the fast path here, advance-by-r is so slow, eh\n            if (rs < 0) return stbi__err(\"bad huffman code\",\"Corrupt JPEG\");\n            s = rs & 15;\n            r = rs >> 4;\n            if (s == 0) {\n               if (r < 15) {\n                  j->eob_run = (1 << r) - 1;\n                  if (r)\n                     j->eob_run += stbi__jpeg_get_bits(j, r);\n                  r = 64; // force end of block\n               } else {\n                  // r=15 s=0 should write 16 0s, so we just do\n                  // a run of 15 0s and then write s (which is 0),\n                  // so we don't have to do anything special here\n               }\n            } else {\n               if (s != 1) return stbi__err(\"bad huffman code\", \"Corrupt JPEG\");\n               // sign bit\n               if (stbi__jpeg_get_bit(j))\n                  s = bit;\n               else\n                  s = -bit;\n            }\n\n            // advance by r\n            while (k <= j->spec_end) {\n               short *p = &data[stbi__jpeg_dezigzag[k++]];\n               if (*p != 0) {\n                  if (stbi__jpeg_get_bit(j))\n                     if ((*p & bit)==0) {\n                        if (*p > 0)\n                           *p += bit;\n                        else\n                           *p -= bit;\n                     }\n               } else {\n                  if (r == 0) {\n                     *p = (short) s;\n                     break;\n                  }\n                  --r;\n               }\n            }\n         } while (k <= j->spec_end);\n      }\n   }\n   return 1;\n}\n\n// take a -128..127 value and stbi__clamp it and convert to 0..255\nstbi_inline static stbi_uc stbi__clamp(int x)\n{\n   // trick to use a single test to catch both cases\n   if ((unsigned int) x > 255) {\n      if (x < 0) return 0;\n      if (x > 255) return 255;\n   }\n   return (stbi_uc) x;\n}\n\n#define stbi__f2f(x)  ((int) (((x) * 4096 + 0.5)))\n#define stbi__fsh(x)  ((x) * 4096)\n\n// derived from jidctint -- DCT_ISLOW\n#define STBI__IDCT_1D(s0,s1,s2,s3,s4,s5,s6,s7) \\\n   int t0,t1,t2,t3,p1,p2,p3,p4,p5,x0,x1,x2,x3; \\\n   p2 = s2;                                    \\\n   p3 = s6;                                    \\\n   p1 = (p2+p3) * stbi__f2f(0.5411961f);       \\\n   t2 = p1 + p3*stbi__f2f(-1.847759065f);      \\\n   t3 = p1 + p2*stbi__f2f( 0.765366865f);      \\\n   p2 = s0;                                    \\\n   p3 = s4;                                    \\\n   t0 = stbi__fsh(p2+p3);                      \\\n   t1 = stbi__fsh(p2-p3);                      \\\n   x0 = t0+t3;                                 \\\n   x3 = t0-t3;                                 \\\n   x1 = t1+t2;                                 \\\n   x2 = t1-t2;                                 \\\n   t0 = s7;                                    \\\n   t1 = s5;                                    \\\n   t2 = s3;                                    \\\n   t3 = s1;                                    \\\n   p3 = t0+t2;                                 \\\n   p4 = t1+t3;                                 \\\n   p1 = t0+t3;                                 \\\n   p2 = t1+t2;                                 \\\n   p5 = (p3+p4)*stbi__f2f( 1.175875602f);      \\\n   t0 = t0*stbi__f2f( 0.298631336f);           \\\n   t1 = t1*stbi__f2f( 2.053119869f);           \\\n   t2 = t2*stbi__f2f( 3.072711026f);           \\\n   t3 = t3*stbi__f2f( 1.501321110f);           \\\n   p1 = p5 + p1*stbi__f2f(-0.899976223f);      \\\n   p2 = p5 + p2*stbi__f2f(-2.562915447f);      \\\n   p3 = p3*stbi__f2f(-1.961570560f);           \\\n   p4 = p4*stbi__f2f(-0.390180644f);           \\\n   t3 += p1+p4;                                \\\n   t2 += p2+p3;                                \\\n   t1 += p2+p4;                                \\\n   t0 += p1+p3;\n\nstatic void stbi__idct_block(stbi_uc *out, int out_stride, short data[64])\n{\n   int i,val[64],*v=val;\n   stbi_uc *o;\n   short *d = data;\n\n   // columns\n   for (i=0; i < 8; ++i,++d, ++v) {\n      // if all zeroes, shortcut -- this avoids dequantizing 0s and IDCTing\n      if (d[ 8]==0 && d[16]==0 && d[24]==0 && d[32]==0\n           && d[40]==0 && d[48]==0 && d[56]==0) {\n         //    no shortcut                 0     seconds\n         //    (1|2|3|4|5|6|7)==0          0     seconds\n         //    all separate               -0.047 seconds\n         //    1 && 2|3 && 4|5 && 6|7:    -0.047 seconds\n         int dcterm = d[0]*4;\n         v[0] = v[8] = v[16] = v[24] = v[32] = v[40] = v[48] = v[56] = dcterm;\n      } else {\n         STBI__IDCT_1D(d[ 0],d[ 8],d[16],d[24],d[32],d[40],d[48],d[56])\n         // constants scaled things up by 1<<12; let's bring them back\n         // down, but keep 2 extra bits of precision\n         x0 += 512; x1 += 512; x2 += 512; x3 += 512;\n         v[ 0] = (x0+t3) >> 10;\n         v[56] = (x0-t3) >> 10;\n         v[ 8] = (x1+t2) >> 10;\n         v[48] = (x1-t2) >> 10;\n         v[16] = (x2+t1) >> 10;\n         v[40] = (x2-t1) >> 10;\n         v[24] = (x3+t0) >> 10;\n         v[32] = (x3-t0) >> 10;\n      }\n   }\n\n   for (i=0, v=val, o=out; i < 8; ++i,v+=8,o+=out_stride) {\n      // no fast case since the first 1D IDCT spread components out\n      STBI__IDCT_1D(v[0],v[1],v[2],v[3],v[4],v[5],v[6],v[7])\n      // constants scaled things up by 1<<12, plus we had 1<<2 from first\n      // loop, plus horizontal and vertical each scale by sqrt(8) so together\n      // we've got an extra 1<<3, so 1<<17 total we need to remove.\n      // so we want to round that, which means adding 0.5 * 1<<17,\n      // aka 65536. Also, we'll end up with -128 to 127 that we want\n      // to encode as 0..255 by adding 128, so we'll add that before the shift\n      x0 += 65536 + (128<<17);\n      x1 += 65536 + (128<<17);\n      x2 += 65536 + (128<<17);\n      x3 += 65536 + (128<<17);\n      // tried computing the shifts into temps, or'ing the temps to see\n      // if any were out of range, but that was slower\n      o[0] = stbi__clamp((x0+t3) >> 17);\n      o[7] = stbi__clamp((x0-t3) >> 17);\n      o[1] = stbi__clamp((x1+t2) >> 17);\n      o[6] = stbi__clamp((x1-t2) >> 17);\n      o[2] = stbi__clamp((x2+t1) >> 17);\n      o[5] = stbi__clamp((x2-t1) >> 17);\n      o[3] = stbi__clamp((x3+t0) >> 17);\n      o[4] = stbi__clamp((x3-t0) >> 17);\n   }\n}\n\n\n\n#define STBI__MARKER_none  0xff\n// if there's a pending marker from the entropy stream, return that\n// otherwise, fetch from the stream and get a marker. if there's no\n// marker, return 0xff, which is never a valid marker value\nstatic stbi_uc stbi__get_marker(stbi__jpeg *j)\n{\n   stbi_uc x;\n   if (j->marker != STBI__MARKER_none) { x = j->marker; j->marker = STBI__MARKER_none; return x; }\n   x = stbi__get8(j->s);\n   if (x != 0xff) return STBI__MARKER_none;\n   while (x == 0xff)\n      x = stbi__get8(j->s); // consume repeated 0xff fill bytes\n   return x;\n}\n\n// in each scan, we'll have scan_n components, and the order\n// of the components is specified by order[]\n#define STBI__RESTART(x)     ((x) >= 0xd0 && (x) <= 0xd7)\n\n// after a restart interval, stbi__jpeg_reset the entropy decoder and\n// the dc prediction\nstatic void stbi__jpeg_reset(stbi__jpeg *j)\n{\n   j->code_bits = 0;\n   j->code_buffer = 0;\n   j->nomore = 0;\n   j->img_comp[0].dc_pred = j->img_comp[1].dc_pred = j->img_comp[2].dc_pred = j->img_comp[3].dc_pred = 0;\n   j->marker = STBI__MARKER_none;\n   j->todo = j->restart_interval ? j->restart_interval : 0x7fffffff;\n   j->eob_run = 0;\n   // no more than 1<<31 MCUs if no restart_interal? that's plenty safe,\n   // since we don't even allow 1<<30 pixels\n}\n\nstatic int stbi__parse_entropy_coded_data(stbi__jpeg *z)\n{\n   stbi__jpeg_reset(z);\n   if (!z->progressive) {\n      if (z->scan_n == 1) {\n         int i,j;\n         STBI_SIMD_ALIGN(short, data[64]);\n         int n = z->order[0];\n         // non-interleaved data, we just need to process one block at a time,\n         // in trivial scanline order\n         // number of blocks to do just depends on how many actual \"pixels\" this\n         // component has, independent of interleaved MCU blocking and such\n         int w = (z->img_comp[n].x+7) >> 3;\n         int h = (z->img_comp[n].y+7) >> 3;\n         for (j=0; j < h; ++j) {\n            for (i=0; i < w; ++i) {\n               int ha = z->img_comp[n].ha;\n               if (!stbi__jpeg_decode_block(z, data, z->huff_dc+z->img_comp[n].hd, z->huff_ac+ha, z->fast_ac[ha], n, z->dequant[z->img_comp[n].tq])) return 0;\n               z->idct_block_kernel(z->img_comp[n].data+z->img_comp[n].w2*j*8+i*8, z->img_comp[n].w2, data);\n               // every data block is an MCU, so countdown the restart interval\n               if (--z->todo <= 0) {\n                  if (z->code_bits < 24) stbi__grow_buffer_unsafe(z);\n                  // if it's NOT a restart, then just bail, so we get corrupt data\n                  // rather than no data\n                  if (!STBI__RESTART(z->marker)) return 1;\n                  stbi__jpeg_reset(z);\n               }\n            }\n         }\n         return 1;\n      } else { // interleaved\n         int i,j,k,x,y;\n         STBI_SIMD_ALIGN(short, data[64]);\n         for (j=0; j < z->img_mcu_y; ++j) {\n            for (i=0; i < z->img_mcu_x; ++i) {\n               // scan an interleaved mcu... process scan_n components in order\n               for (k=0; k < z->scan_n; ++k) {\n                  int n = z->order[k];\n                  // scan out an mcu's worth of this component; that's just determined\n                  // by the basic H and V specified for the component\n                  for (y=0; y < z->img_comp[n].v; ++y) {\n                     for (x=0; x < z->img_comp[n].h; ++x) {\n                        int x2 = (i*z->img_comp[n].h + x)*8;\n                        int y2 = (j*z->img_comp[n].v + y)*8;\n                        int ha = z->img_comp[n].ha;\n                        if (!stbi__jpeg_decode_block(z, data, z->huff_dc+z->img_comp[n].hd, z->huff_ac+ha, z->fast_ac[ha], n, z->dequant[z->img_comp[n].tq])) return 0;\n                        z->idct_block_kernel(z->img_comp[n].data+z->img_comp[n].w2*y2+x2, z->img_comp[n].w2, data);\n                     }\n                  }\n               }\n               // after all interleaved components, that's an interleaved MCU,\n               // so now count down the restart interval\n               if (--z->todo <= 0) {\n                  if (z->code_bits < 24) stbi__grow_buffer_unsafe(z);\n                  if (!STBI__RESTART(z->marker)) return 1;\n                  stbi__jpeg_reset(z);\n               }\n            }\n         }\n         return 1;\n      }\n   } else {\n      if (z->scan_n == 1) {\n         int i,j;\n         int n = z->order[0];\n         // non-interleaved data, we just need to process one block at a time,\n         // in trivial scanline order\n         // number of blocks to do just depends on how many actual \"pixels\" this\n         // component has, independent of interleaved MCU blocking and such\n         int w = (z->img_comp[n].x+7) >> 3;\n         int h = (z->img_comp[n].y+7) >> 3;\n         for (j=0; j < h; ++j) {\n            for (i=0; i < w; ++i) {\n               short *data = z->img_comp[n].coeff + 64 * (i + j * z->img_comp[n].coeff_w);\n               if (z->spec_start == 0) {\n                  if (!stbi__jpeg_decode_block_prog_dc(z, data, &z->huff_dc[z->img_comp[n].hd], n))\n                     return 0;\n               } else {\n                  int ha = z->img_comp[n].ha;\n                  if (!stbi__jpeg_decode_block_prog_ac(z, data, &z->huff_ac[ha], z->fast_ac[ha]))\n                     return 0;\n               }\n               // every data block is an MCU, so countdown the restart interval\n               if (--z->todo <= 0) {\n                  if (z->code_bits < 24) stbi__grow_buffer_unsafe(z);\n                  if (!STBI__RESTART(z->marker)) return 1;\n                  stbi__jpeg_reset(z);\n               }\n            }\n         }\n         return 1;\n      } else { // interleaved\n         int i,j,k,x,y;\n         for (j=0; j < z->img_mcu_y; ++j) {\n            for (i=0; i < z->img_mcu_x; ++i) {\n               // scan an interleaved mcu... process scan_n components in order\n               for (k=0; k < z->scan_n; ++k) {\n                  int n = z->order[k];\n                  // scan out an mcu's worth of this component; that's just determined\n                  // by the basic H and V specified for the component\n                  for (y=0; y < z->img_comp[n].v; ++y) {\n                     for (x=0; x < z->img_comp[n].h; ++x) {\n                        int x2 = (i*z->img_comp[n].h + x);\n                        int y2 = (j*z->img_comp[n].v + y);\n                        short *data = z->img_comp[n].coeff + 64 * (x2 + y2 * z->img_comp[n].coeff_w);\n                        if (!stbi__jpeg_decode_block_prog_dc(z, data, &z->huff_dc[z->img_comp[n].hd], n))\n                           return 0;\n                     }\n                  }\n               }\n               // after all interleaved components, that's an interleaved MCU,\n               // so now count down the restart interval\n               if (--z->todo <= 0) {\n                  if (z->code_bits < 24) stbi__grow_buffer_unsafe(z);\n                  if (!STBI__RESTART(z->marker)) return 1;\n                  stbi__jpeg_reset(z);\n               }\n            }\n         }\n         return 1;\n      }\n   }\n}\n\nstatic void stbi__jpeg_dequantize(short *data, stbi__uint16 *dequant)\n{\n   int i;\n   for (i=0; i < 64; ++i)\n      data[i] *= dequant[i];\n}\n\nstatic void stbi__jpeg_finish(stbi__jpeg *z)\n{\n   if (z->progressive) {\n      // dequantize and idct the data\n      int i,j,n;\n      for (n=0; n < z->s->img_n; ++n) {\n         int w = (z->img_comp[n].x+7) >> 3;\n         int h = (z->img_comp[n].y+7) >> 3;\n         for (j=0; j < h; ++j) {\n            for (i=0; i < w; ++i) {\n               short *data = z->img_comp[n].coeff + 64 * (i + j * z->img_comp[n].coeff_w);\n               stbi__jpeg_dequantize(data, z->dequant[z->img_comp[n].tq]);\n               z->idct_block_kernel(z->img_comp[n].data+z->img_comp[n].w2*j*8+i*8, z->img_comp[n].w2, data);\n            }\n         }\n      }\n   }\n}\n\nstatic int stbi__process_marker(stbi__jpeg *z, int m)\n{\n   int L;\n   switch (m) {\n      case STBI__MARKER_none: // no marker found\n         return stbi__err(\"expected marker\",\"Corrupt JPEG\");\n\n      case 0xDD: // DRI - specify restart interval\n         if (stbi__get16be(z->s) != 4) return stbi__err(\"bad DRI len\",\"Corrupt JPEG\");\n         z->restart_interval = stbi__get16be(z->s);\n         return 1;\n\n      case 0xDB: // DQT - define quantization table\n         L = stbi__get16be(z->s)-2;\n         while (L > 0) {\n            int q = stbi__get8(z->s);\n            int p = q >> 4, sixteen = (p != 0);\n            int t = q & 15,i;\n            if (p != 0 && p != 1) return stbi__err(\"bad DQT type\",\"Corrupt JPEG\");\n            if (t > 3) return stbi__err(\"bad DQT table\",\"Corrupt JPEG\");\n\n            for (i=0; i < 64; ++i)\n               z->dequant[t][stbi__jpeg_dezigzag[i]] = (stbi__uint16)(sixteen ? stbi__get16be(z->s) : stbi__get8(z->s));\n            L -= (sixteen ? 129 : 65);\n         }\n         return L==0;\n\n      case 0xC4: // DHT - define huffman table\n         L = stbi__get16be(z->s)-2;\n         while (L > 0) {\n            stbi_uc *v;\n            int sizes[16],i,n=0;\n            int q = stbi__get8(z->s);\n            int tc = q >> 4;\n            int th = q & 15;\n            if (tc > 1 || th > 3) return stbi__err(\"bad DHT header\",\"Corrupt JPEG\");\n            for (i=0; i < 16; ++i) {\n               sizes[i] = stbi__get8(z->s);\n               n += sizes[i];\n            }\n            L -= 17;\n            if (tc == 0) {\n               if (!stbi__build_huffman(z->huff_dc+th, sizes)) return 0;\n               v = z->huff_dc[th].values;\n            } else {\n               if (!stbi__build_huffman(z->huff_ac+th, sizes)) return 0;\n               v = z->huff_ac[th].values;\n            }\n            for (i=0; i < n; ++i)\n               v[i] = stbi__get8(z->s);\n            if (tc != 0)\n               stbi__build_fast_ac(z->fast_ac[th], z->huff_ac + th);\n            L -= n;\n         }\n         return L==0;\n   }\n\n   // check for comment block or APP blocks\n   if ((m >= 0xE0 && m <= 0xEF) || m == 0xFE) {\n      L = stbi__get16be(z->s);\n      if (L < 2) {\n         if (m == 0xFE)\n            return stbi__err(\"bad COM len\",\"Corrupt JPEG\");\n         else\n            return stbi__err(\"bad APP len\",\"Corrupt JPEG\");\n      }\n      L -= 2;\n\n      if (m == 0xE0 && L >= 5) { // JFIF APP0 segment\n         static const unsigned char tag[5] = {'J','F','I','F','\\0'};\n         int ok = 1;\n         int i;\n         for (i=0; i < 5; ++i)\n            if (stbi__get8(z->s) != tag[i])\n               ok = 0;\n         L -= 5;\n         if (ok)\n            z->jfif = 1;\n      } else if (m == 0xEE && L >= 12) { // Adobe APP14 segment\n         static const unsigned char tag[6] = {'A','d','o','b','e','\\0'};\n         int ok = 1;\n         int i;\n         for (i=0; i < 6; ++i)\n            if (stbi__get8(z->s) != tag[i])\n               ok = 0;\n         L -= 6;\n         if (ok) {\n            stbi__get8(z->s); // version\n            stbi__get16be(z->s); // flags0\n            stbi__get16be(z->s); // flags1\n            z->app14_color_transform = stbi__get8(z->s); // color transform\n            L -= 6;\n         }\n      }\n\n      stbi__skip(z->s, L);\n      return 1;\n   }\n\n   return stbi__err(\"unknown marker\",\"Corrupt JPEG\");\n}\n\n// after we see SOS\nstatic int stbi__process_scan_header(stbi__jpeg *z)\n{\n   int i;\n   int Ls = stbi__get16be(z->s);\n   z->scan_n = stbi__get8(z->s);\n   if (z->scan_n < 1 || z->scan_n > 4 || z->scan_n > (int) z->s->img_n) return stbi__err(\"bad SOS component count\",\"Corrupt JPEG\");\n   if (Ls != 6+2*z->scan_n) return stbi__err(\"bad SOS len\",\"Corrupt JPEG\");\n   for (i=0; i < z->scan_n; ++i) {\n      int id = stbi__get8(z->s), which;\n      int q = stbi__get8(z->s);\n      for (which = 0; which < z->s->img_n; ++which)\n         if (z->img_comp[which].id == id)\n            break;\n      if (which == z->s->img_n) return 0; // no match\n      z->img_comp[which].hd = q >> 4;   if (z->img_comp[which].hd > 3) return stbi__err(\"bad DC huff\",\"Corrupt JPEG\");\n      z->img_comp[which].ha = q & 15;   if (z->img_comp[which].ha > 3) return stbi__err(\"bad AC huff\",\"Corrupt JPEG\");\n      z->order[i] = which;\n   }\n\n   {\n      int aa;\n      z->spec_start = stbi__get8(z->s);\n      z->spec_end   = stbi__get8(z->s); // should be 63, but might be 0\n      aa = stbi__get8(z->s);\n      z->succ_high = (aa >> 4);\n      z->succ_low  = (aa & 15);\n      if (z->progressive) {\n         if (z->spec_start > 63 || z->spec_end > 63  || z->spec_start > z->spec_end || z->succ_high > 13 || z->succ_low > 13)\n            return stbi__err(\"bad SOS\", \"Corrupt JPEG\");\n      } else {\n         if (z->spec_start != 0) return stbi__err(\"bad SOS\",\"Corrupt JPEG\");\n         if (z->succ_high != 0 || z->succ_low != 0) return stbi__err(\"bad SOS\",\"Corrupt JPEG\");\n         z->spec_end = 63;\n      }\n   }\n\n   return 1;\n}\n\nstatic int stbi__free_jpeg_components(stbi__jpeg *z, int ncomp, int why)\n{\n   int i;\n   for (i=0; i < ncomp; ++i) {\n      if (z->img_comp[i].raw_data) {\n         STBI_FREE(z->img_comp[i].raw_data);\n         z->img_comp[i].raw_data = NULL;\n         z->img_comp[i].data = NULL;\n      }\n      if (z->img_comp[i].raw_coeff) {\n         STBI_FREE(z->img_comp[i].raw_coeff);\n         z->img_comp[i].raw_coeff = 0;\n         z->img_comp[i].coeff = 0;\n      }\n      if (z->img_comp[i].linebuf) {\n         STBI_FREE(z->img_comp[i].linebuf);\n         z->img_comp[i].linebuf = NULL;\n      }\n   }\n   return why;\n}\n\nstatic int stbi__process_frame_header(stbi__jpeg *z, int scan)\n{\n   stbi__context *s = z->s;\n   int Lf,p,i,q, h_max=1,v_max=1,c;\n   Lf = stbi__get16be(s);         if (Lf < 11) return stbi__err(\"bad SOF len\",\"Corrupt JPEG\"); // JPEG\n   p  = stbi__get8(s);            if (p != 8) return stbi__err(\"only 8-bit\",\"JPEG format not supported: 8-bit only\"); // JPEG baseline\n   s->img_y = stbi__get16be(s);   if (s->img_y == 0) return stbi__err(\"no header height\", \"JPEG format not supported: delayed height\"); // Legal, but we don't handle it--but neither does IJG\n   s->img_x = stbi__get16be(s);   if (s->img_x == 0) return stbi__err(\"0 width\",\"Corrupt JPEG\"); // JPEG requires\n   c = stbi__get8(s);\n   if (c != 3 && c != 1 && c != 4) return stbi__err(\"bad component count\",\"Corrupt JPEG\");\n   s->img_n = c;\n   for (i=0; i < c; ++i) {\n      z->img_comp[i].data = NULL;\n      z->img_comp[i].linebuf = NULL;\n   }\n\n   if (Lf != 8+3*s->img_n) return stbi__err(\"bad SOF len\",\"Corrupt JPEG\");\n\n   z->rgb = 0;\n   for (i=0; i < s->img_n; ++i) {\n      static const unsigned char rgb[3] = { 'R', 'G', 'B' };\n      z->img_comp[i].id = stbi__get8(s);\n      if (s->img_n == 3 && z->img_comp[i].id == rgb[i])\n         ++z->rgb;\n      q = stbi__get8(s);\n      z->img_comp[i].h = (q >> 4);  if (!z->img_comp[i].h || z->img_comp[i].h > 4) return stbi__err(\"bad H\",\"Corrupt JPEG\");\n      z->img_comp[i].v = q & 15;    if (!z->img_comp[i].v || z->img_comp[i].v > 4) return stbi__err(\"bad V\",\"Corrupt JPEG\");\n      z->img_comp[i].tq = stbi__get8(s);  if (z->img_comp[i].tq > 3) return stbi__err(\"bad TQ\",\"Corrupt JPEG\");\n   }\n\n   if (scan != STBI__SCAN_load) return 1;\n\n   if (!stbi__mad3sizes_valid(s->img_x, s->img_y, s->img_n, 0)) return stbi__err(\"too large\", \"Image too large to decode\");\n\n   for (i=0; i < s->img_n; ++i) {\n      if (z->img_comp[i].h > h_max) h_max = z->img_comp[i].h;\n      if (z->img_comp[i].v > v_max) v_max = z->img_comp[i].v;\n   }\n\n   // compute interleaved mcu info\n   z->img_h_max = h_max;\n   z->img_v_max = v_max;\n   z->img_mcu_w = h_max * 8;\n   z->img_mcu_h = v_max * 8;\n   // these sizes can't be more than 17 bits\n   z->img_mcu_x = (s->img_x + z->img_mcu_w-1) / z->img_mcu_w;\n   z->img_mcu_y = (s->img_y + z->img_mcu_h-1) / z->img_mcu_h;\n\n   for (i=0; i < s->img_n; ++i) {\n      // number of effective pixels (e.g. for non-interleaved MCU)\n      z->img_comp[i].x = (s->img_x * z->img_comp[i].h + h_max-1) / h_max;\n      z->img_comp[i].y = (s->img_y * z->img_comp[i].v + v_max-1) / v_max;\n      // to simplify generation, we'll allocate enough memory to decode\n      // the bogus oversized data from using interleaved MCUs and their\n      // big blocks (e.g. a 16x16 iMCU on an image of width 33); we won't\n      // discard the extra data until colorspace conversion\n      //\n      // img_mcu_x, img_mcu_y: <=17 bits; comp[i].h and .v are <=4 (checked earlier)\n      // so these muls can't overflow with 32-bit ints (which we require)\n      z->img_comp[i].w2 = z->img_mcu_x * z->img_comp[i].h * 8;\n      z->img_comp[i].h2 = z->img_mcu_y * z->img_comp[i].v * 8;\n      z->img_comp[i].coeff = 0;\n      z->img_comp[i].raw_coeff = 0;\n      z->img_comp[i].linebuf = NULL;\n      z->img_comp[i].raw_data = stbi__malloc_mad2(z->img_comp[i].w2, z->img_comp[i].h2, 15);\n      if (z->img_comp[i].raw_data == NULL)\n         return stbi__free_jpeg_components(z, i+1, stbi__err(\"outofmem\", \"Out of memory\"));\n      // align blocks for idct using mmx/sse\n      z->img_comp[i].data = (stbi_uc*) (((size_t) z->img_comp[i].raw_data + 15) & ~15);\n      if (z->progressive) {\n         // w2, h2 are multiples of 8 (see above)\n         z->img_comp[i].coeff_w = z->img_comp[i].w2 / 8;\n         z->img_comp[i].coeff_h = z->img_comp[i].h2 / 8;\n         z->img_comp[i].raw_coeff = stbi__malloc_mad3(z->img_comp[i].w2, z->img_comp[i].h2, sizeof(short), 15);\n         if (z->img_comp[i].raw_coeff == NULL)\n            return stbi__free_jpeg_components(z, i+1, stbi__err(\"outofmem\", \"Out of memory\"));\n         z->img_comp[i].coeff = (short*) (((size_t) z->img_comp[i].raw_coeff + 15) & ~15);\n      }\n   }\n\n   return 1;\n}\n\n// use comparisons since in some cases we handle more than one case (e.g. SOF)\n#define stbi__DNL(x)         ((x) == 0xdc)\n#define stbi__SOI(x)         ((x) == 0xd8)\n#define stbi__EOI(x)         ((x) == 0xd9)\n#define stbi__SOF(x)         ((x) == 0xc0 || (x) == 0xc1 || (x) == 0xc2)\n#define stbi__SOS(x)         ((x) == 0xda)\n\n#define stbi__SOF_progressive(x)   ((x) == 0xc2)\n\nstatic int stbi__decode_jpeg_header(stbi__jpeg *z, int scan)\n{\n   int m;\n   z->jfif = 0;\n   z->app14_color_transform = -1; // valid values are 0,1,2\n   z->marker = STBI__MARKER_none; // initialize cached marker to empty\n   m = stbi__get_marker(z);\n   if (!stbi__SOI(m)) return stbi__err(\"no SOI\",\"Corrupt JPEG\");\n   if (scan == STBI__SCAN_type) return 1;\n   m = stbi__get_marker(z);\n   while (!stbi__SOF(m)) {\n      if (!stbi__process_marker(z,m)) return 0;\n      m = stbi__get_marker(z);\n      while (m == STBI__MARKER_none) {\n         // some files have extra padding after their blocks, so ok, we'll scan\n         if (stbi__at_eof(z->s)) return stbi__err(\"no SOF\", \"Corrupt JPEG\");\n         m = stbi__get_marker(z);\n      }\n   }\n   z->progressive = stbi__SOF_progressive(m);\n   if (!stbi__process_frame_header(z, scan)) return 0;\n   return 1;\n}\n\n// decode image to YCbCr format\nstatic int stbi__decode_jpeg_image(stbi__jpeg *j)\n{\n   int m;\n   for (m = 0; m < 4; m++) {\n      j->img_comp[m].raw_data = NULL;\n      j->img_comp[m].raw_coeff = NULL;\n   }\n   j->restart_interval = 0;\n   if (!stbi__decode_jpeg_header(j, STBI__SCAN_load)) return 0;\n   m = stbi__get_marker(j);\n   while (!stbi__EOI(m)) {\n      if (stbi__SOS(m)) {\n         if (!stbi__process_scan_header(j)) return 0;\n         if (!stbi__parse_entropy_coded_data(j)) return 0;\n         if (j->marker == STBI__MARKER_none ) {\n            // handle 0s at the end of image data from IP Kamera 9060\n            while (!stbi__at_eof(j->s)) {\n               int x = stbi__get8(j->s);\n               if (x == 255) {\n                  j->marker = stbi__get8(j->s);\n                  break;\n               }\n            }\n            // if we reach eof without hitting a marker, stbi__get_marker() below will fail and we'll eventually return 0\n         }\n      } else if (stbi__DNL(m)) {\n         int Ld = stbi__get16be(j->s);\n         stbi__uint32 NL = stbi__get16be(j->s);\n         if (Ld != 4) return stbi__err(\"bad DNL len\", \"Corrupt JPEG\");\n         if (NL != j->s->img_y) return stbi__err(\"bad DNL height\", \"Corrupt JPEG\");\n      } else {\n         if (!stbi__process_marker(j, m)) return 0;\n      }\n      m = stbi__get_marker(j);\n   }\n   if (j->progressive)\n      stbi__jpeg_finish(j);\n   return 1;\n}\n\n// static jfif-centered resampling (across block boundaries)\n\ntypedef stbi_uc *(*resample_row_func)(stbi_uc *out, stbi_uc *in0, stbi_uc *in1,\n                                    int w, int hs);\n\n#define stbi__div4(x) ((stbi_uc) ((x) >> 2))\n\nstatic stbi_uc *resample_row_1(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs)\n{\n   STBI_NOTUSED(out);\n   STBI_NOTUSED(in_far);\n   STBI_NOTUSED(w);\n   STBI_NOTUSED(hs);\n   return in_near;\n}\n\nstatic stbi_uc* stbi__resample_row_v_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs)\n{\n   // need to generate two samples vertically for every one in input\n   int i;\n   STBI_NOTUSED(hs);\n   for (i=0; i < w; ++i)\n      out[i] = stbi__div4(3*in_near[i] + in_far[i] + 2);\n   return out;\n}\n\nstatic stbi_uc*  stbi__resample_row_h_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs)\n{\n   // need to generate two samples horizontally for every one in input\n   int i;\n   stbi_uc *input = in_near;\n\n   if (w == 1) {\n      // if only one sample, can't do any interpolation\n      out[0] = out[1] = input[0];\n      return out;\n   }\n\n   out[0] = input[0];\n   out[1] = stbi__div4(input[0]*3 + input[1] + 2);\n   for (i=1; i < w-1; ++i) {\n      int n = 3*input[i]+2;\n      out[i*2+0] = stbi__div4(n+input[i-1]);\n      out[i*2+1] = stbi__div4(n+input[i+1]);\n   }\n   out[i*2+0] = stbi__div4(input[w-2]*3 + input[w-1] + 2);\n   out[i*2+1] = input[w-1];\n\n   STBI_NOTUSED(in_far);\n   STBI_NOTUSED(hs);\n\n   return out;\n}\n\n#define stbi__div16(x) ((stbi_uc) ((x) >> 4))\n\nstatic stbi_uc *stbi__resample_row_hv_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs)\n{\n   // need to generate 2x2 samples for every one in input\n   int i,t0,t1;\n   if (w == 1) {\n      out[0] = out[1] = stbi__div4(3*in_near[0] + in_far[0] + 2);\n      return out;\n   }\n\n   t1 = 3*in_near[0] + in_far[0];\n   out[0] = stbi__div4(t1+2);\n   for (i=1; i < w; ++i) {\n      t0 = t1;\n      t1 = 3*in_near[i]+in_far[i];\n      out[i*2-1] = stbi__div16(3*t0 + t1 + 8);\n      out[i*2  ] = stbi__div16(3*t1 + t0 + 8);\n   }\n   out[w*2-1] = stbi__div4(t1+2);\n\n   STBI_NOTUSED(hs);\n\n   return out;\n}\n\n\nstatic stbi_uc *stbi__resample_row_generic(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs)\n{\n   // resample with nearest-neighbor\n   int i,j;\n   STBI_NOTUSED(in_far);\n   for (i=0; i < w; ++i)\n      for (j=0; j < hs; ++j)\n         out[i*hs+j] = in_near[i];\n   return out;\n}\n\n// this is a reduced-precision calculation of YCbCr-to-RGB introduced\n// to make sure the code produces the same results in both SIMD and scalar\n#define stbi__float2fixed(x)  (((int) ((x) * 4096.0f + 0.5f)) << 8)\nstatic void stbi__YCbCr_to_RGB_row(stbi_uc *out, const stbi_uc *y, const stbi_uc *pcb, const stbi_uc *pcr, int count, int step)\n{\n   int i;\n   for (i=0; i < count; ++i) {\n      int y_fixed = (y[i] << 20) + (1<<19); // rounding\n      int r,g,b;\n      int cr = pcr[i] - 128;\n      int cb = pcb[i] - 128;\n      r = y_fixed +  cr* stbi__float2fixed(1.40200f);\n      g = y_fixed + (cr*-stbi__float2fixed(0.71414f)) + ((cb*-stbi__float2fixed(0.34414f)) & 0xffff0000);\n      b = y_fixed                                     +   cb* stbi__float2fixed(1.77200f);\n      r >>= 20;\n      g >>= 20;\n      b >>= 20;\n      if ((unsigned) r > 255) { if (r < 0) r = 0; else r = 255; }\n      if ((unsigned) g > 255) { if (g < 0) g = 0; else g = 255; }\n      if ((unsigned) b > 255) { if (b < 0) b = 0; else b = 255; }\n      out[0] = (stbi_uc)r;\n      out[1] = (stbi_uc)g;\n      out[2] = (stbi_uc)b;\n      out[3] = 255;\n      out += step;\n   }\n}\n\n\n// set up the kernels\nstatic void stbi__setup_jpeg(stbi__jpeg *j)\n{\n   j->idct_block_kernel = stbi__idct_block;\n   j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_row;\n   j->resample_row_hv_2_kernel = stbi__resample_row_hv_2;\n\n\n}\n\n// clean up the temporary component buffers\nstatic void stbi__cleanup_jpeg(stbi__jpeg *j)\n{\n   stbi__free_jpeg_components(j, j->s->img_n, 0);\n}\n\ntypedef struct\n{\n   resample_row_func resample;\n   stbi_uc *line0,*line1;\n   int hs,vs;   // expansion factor in each axis\n   int w_lores; // horizontal pixels pre-expansion\n   int ystep;   // how far through vertical expansion we are\n   int ypos;    // which pre-expansion row we're on\n} stbi__resample;\n\n// fast 0..255 * 0..255 => 0..255 rounded multiplication\nstatic stbi_uc stbi__blinn_8x8(stbi_uc x, stbi_uc y)\n{\n   unsigned int t = x*y + 128;\n   return (stbi_uc) ((t + (t >>8)) >> 8);\n}\n\nstatic stbi_uc *load_jpeg_image(stbi__jpeg *z, int *out_x, int *out_y, int *comp, int req_comp)\n{\n   int n, decode_n, is_rgb;\n   z->s->img_n = 0; // make stbi__cleanup_jpeg safe\n\n   // validate req_comp\n   if (req_comp < 0 || req_comp > 4) return stbi__errpuc(\"bad req_comp\", \"Internal error\");\n\n   // load a jpeg image from whichever source, but leave in YCbCr format\n   if (!stbi__decode_jpeg_image(z)) { stbi__cleanup_jpeg(z); return NULL; }\n\n   // determine actual number of components to generate\n   n = req_comp ? req_comp : z->s->img_n >= 3 ? 3 : 1;\n\n   is_rgb = z->s->img_n == 3 && (z->rgb == 3 || (z->app14_color_transform == 0 && !z->jfif));\n\n   if (z->s->img_n == 3 && n < 3 && !is_rgb)\n      decode_n = 1;\n   else\n      decode_n = z->s->img_n;\n\n   // resample and color-convert\n   {\n      int k;\n      unsigned int i,j;\n      stbi_uc *output;\n      stbi_uc *coutput[4];\n\n      stbi__resample res_comp[4];\n\n      for (k=0; k < decode_n; ++k) {\n         stbi__resample *r = &res_comp[k];\n\n         // allocate line buffer big enough for upsampling off the edges\n         // with upsample factor of 4\n         z->img_comp[k].linebuf = (stbi_uc *) stbi__malloc(z->s->img_x + 3);\n         if (!z->img_comp[k].linebuf) { stbi__cleanup_jpeg(z); return stbi__errpuc(\"outofmem\", \"Out of memory\"); }\n\n         r->hs      = z->img_h_max / z->img_comp[k].h;\n         r->vs      = z->img_v_max / z->img_comp[k].v;\n         r->ystep   = r->vs >> 1;\n         r->w_lores = (z->s->img_x + r->hs-1) / r->hs;\n         r->ypos    = 0;\n         r->line0   = r->line1 = z->img_comp[k].data;\n\n         if      (r->hs == 1 && r->vs == 1) r->resample = resample_row_1;\n         else if (r->hs == 1 && r->vs == 2) r->resample = stbi__resample_row_v_2;\n         else if (r->hs == 2 && r->vs == 1) r->resample = stbi__resample_row_h_2;\n         else if (r->hs == 2 && r->vs == 2) r->resample = z->resample_row_hv_2_kernel;\n         else                               r->resample = stbi__resample_row_generic;\n      }\n\n      // can't error after this so, this is safe\n      output = (stbi_uc *) stbi__malloc_mad3(n, z->s->img_x, z->s->img_y, 1);\n      if (!output) { stbi__cleanup_jpeg(z); return stbi__errpuc(\"outofmem\", \"Out of memory\"); }\n\n      // now go ahead and resample\n      for (j=0; j < z->s->img_y; ++j) {\n         stbi_uc *out = output + n * z->s->img_x * j;\n         for (k=0; k < decode_n; ++k) {\n            stbi__resample *r = &res_comp[k];\n            int y_bot = r->ystep >= (r->vs >> 1);\n            coutput[k] = r->resample(z->img_comp[k].linebuf,\n                                     y_bot ? r->line1 : r->line0,\n                                     y_bot ? r->line0 : r->line1,\n                                     r->w_lores, r->hs);\n            if (++r->ystep >= r->vs) {\n               r->ystep = 0;\n               r->line0 = r->line1;\n               if (++r->ypos < z->img_comp[k].y)\n                  r->line1 += z->img_comp[k].w2;\n            }\n         }\n         if (n >= 3) {\n            stbi_uc *y = coutput[0];\n            if (z->s->img_n == 3) {\n               if (is_rgb) {\n                  for (i=0; i < z->s->img_x; ++i) {\n                     out[0] = y[i];\n                     out[1] = coutput[1][i];\n                     out[2] = coutput[2][i];\n                     out[3] = 255;\n                     out += n;\n                  }\n               } else {\n                  z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n);\n               }\n            } else if (z->s->img_n == 4) {\n               if (z->app14_color_transform == 0) { // CMYK\n                  for (i=0; i < z->s->img_x; ++i) {\n                     stbi_uc m = coutput[3][i];\n                     out[0] = stbi__blinn_8x8(coutput[0][i], m);\n                     out[1] = stbi__blinn_8x8(coutput[1][i], m);\n                     out[2] = stbi__blinn_8x8(coutput[2][i], m);\n                     out[3] = 255;\n                     out += n;\n                  }\n               } else if (z->app14_color_transform == 2) { // YCCK\n                  z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n);\n                  for (i=0; i < z->s->img_x; ++i) {\n                     stbi_uc m = coutput[3][i];\n                     out[0] = stbi__blinn_8x8(255 - out[0], m);\n                     out[1] = stbi__blinn_8x8(255 - out[1], m);\n                     out[2] = stbi__blinn_8x8(255 - out[2], m);\n                     out += n;\n                  }\n               } else { // YCbCr + alpha?  Ignore the fourth channel for now\n                  z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n);\n               }\n            } else\n               for (i=0; i < z->s->img_x; ++i) {\n                  out[0] = out[1] = out[2] = y[i];\n                  out[3] = 255; // not used if n==3\n                  out += n;\n               }\n         } else {\n            if (is_rgb) {\n               if (n == 1)\n                  for (i=0; i < z->s->img_x; ++i)\n                     *out++ = stbi__compute_y(coutput[0][i], coutput[1][i], coutput[2][i]);\n               else {\n                  for (i=0; i < z->s->img_x; ++i, out += 2) {\n                     out[0] = stbi__compute_y(coutput[0][i], coutput[1][i], coutput[2][i]);\n                     out[1] = 255;\n                  }\n               }\n            } else if (z->s->img_n == 4 && z->app14_color_transform == 0) {\n               for (i=0; i < z->s->img_x; ++i) {\n                  stbi_uc m = coutput[3][i];\n                  stbi_uc r = stbi__blinn_8x8(coutput[0][i], m);\n                  stbi_uc g = stbi__blinn_8x8(coutput[1][i], m);\n                  stbi_uc b = stbi__blinn_8x8(coutput[2][i], m);\n                  out[0] = stbi__compute_y(r, g, b);\n                  out[1] = 255;\n                  out += n;\n               }\n            } else if (z->s->img_n == 4 && z->app14_color_transform == 2) {\n               for (i=0; i < z->s->img_x; ++i) {\n                  out[0] = stbi__blinn_8x8(255 - coutput[0][i], coutput[3][i]);\n                  out[1] = 255;\n                  out += n;\n               }\n            } else {\n               stbi_uc *y = coutput[0];\n               if (n == 1)\n                  for (i=0; i < z->s->img_x; ++i) out[i] = y[i];\n               else\n                  for (i=0; i < z->s->img_x; ++i) { *out++ = y[i]; *out++ = 255; }\n            }\n         }\n      }\n      stbi__cleanup_jpeg(z);\n      *out_x = z->s->img_x;\n      *out_y = z->s->img_y;\n      if (comp) *comp = z->s->img_n >= 3 ? 3 : 1; // report original components, not output\n      return output;\n   }\n}\n\nstatic void *stbi__jpeg_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri)\n{\n   unsigned char* result;\n   stbi__jpeg* j = (stbi__jpeg*) stbi__malloc(sizeof(stbi__jpeg));\n   STBI_NOTUSED(ri);\n   j->s = s;\n   stbi__setup_jpeg(j);\n   result = load_jpeg_image(j, x,y,comp,req_comp);\n   STBI_FREE(j);\n   return result;\n}\n\nstatic int stbi__jpeg_test(stbi__context *s)\n{\n   int r;\n   stbi__jpeg* j = (stbi__jpeg*)stbi__malloc(sizeof(stbi__jpeg));\n   j->s = s;\n   stbi__setup_jpeg(j);\n   r = stbi__decode_jpeg_header(j, STBI__SCAN_type);\n   stbi__rewind(s);\n   STBI_FREE(j);\n   return r;\n}\n\nstatic int stbi__jpeg_info_raw(stbi__jpeg *j, int *x, int *y, int *comp)\n{\n   if (!stbi__decode_jpeg_header(j, STBI__SCAN_header)) {\n      stbi__rewind( j->s );\n      return 0;\n   }\n   if (x) *x = j->s->img_x;\n   if (y) *y = j->s->img_y;\n   if (comp) *comp = j->s->img_n >= 3 ? 3 : 1;\n   return 1;\n}\n\nstatic int stbi__jpeg_info(stbi__context *s, int *x, int *y, int *comp)\n{\n   int result;\n   stbi__jpeg* j = (stbi__jpeg*) (stbi__malloc(sizeof(stbi__jpeg)));\n   j->s = s;\n   result = stbi__jpeg_info_raw(j, x, y, comp);\n   STBI_FREE(j);\n   return result;\n}\n\n// public domain zlib decode    v0.2  Sean Barrett 2006-11-18\n//    simple implementation\n//      - all input must be provided in an upfront buffer\n//      - all output is written to a single output buffer (can malloc/realloc)\n//    performance\n//      - fast huffman\n\n#ifndef STBI_NO_ZLIB\n\n// fast-way is faster to check than jpeg huffman, but slow way is slower\n#define STBI__ZFAST_BITS  9 // accelerate all cases in default tables\n#define STBI__ZFAST_MASK  ((1 << STBI__ZFAST_BITS) - 1)\n\n// zlib-style huffman encoding\n// (jpegs packs from left, zlib from right, so can't share code)\ntypedef struct\n{\n   stbi__uint16 fast[1 << STBI__ZFAST_BITS];\n   stbi__uint16 firstcode[16];\n   int maxcode[17];\n   stbi__uint16 firstsymbol[16];\n   stbi_uc  size[288];\n   stbi__uint16 value[288];\n} stbi__zhuffman;\n\nstbi_inline static int stbi__bitreverse16(int n)\n{\n  n = ((n & 0xAAAA) >>  1) | ((n & 0x5555) << 1);\n  n = ((n & 0xCCCC) >>  2) | ((n & 0x3333) << 2);\n  n = ((n & 0xF0F0) >>  4) | ((n & 0x0F0F) << 4);\n  n = ((n & 0xFF00) >>  8) | ((n & 0x00FF) << 8);\n  return n;\n}\n\nstbi_inline static int stbi__bit_reverse(int v, int bits)\n{\n   STBI_ASSERT(bits <= 16);\n   // to bit reverse n bits, reverse 16 and shift\n   // e.g. 11 bits, bit reverse and shift away 5\n   return stbi__bitreverse16(v) >> (16-bits);\n}\n\nstatic int stbi__zbuild_huffman(stbi__zhuffman *z, const stbi_uc *sizelist, int num)\n{\n   int i,k=0;\n   int code, next_code[16], sizes[17];\n\n   // DEFLATE spec for generating codes\n   memset(sizes, 0, sizeof(sizes));\n   memset(z->fast, 0, sizeof(z->fast));\n   for (i=0; i < num; ++i)\n      ++sizes[sizelist[i]];\n   sizes[0] = 0;\n   for (i=1; i < 16; ++i)\n      if (sizes[i] > (1 << i))\n         return stbi__err(\"bad sizes\", \"Corrupt PNG\");\n   code = 0;\n   for (i=1; i < 16; ++i) {\n      next_code[i] = code;\n      z->firstcode[i] = (stbi__uint16) code;\n      z->firstsymbol[i] = (stbi__uint16) k;\n      code = (code + sizes[i]);\n      if (sizes[i])\n         if (code-1 >= (1 << i)) return stbi__err(\"bad codelengths\",\"Corrupt PNG\");\n      z->maxcode[i] = code << (16-i); // preshift for inner loop\n      code <<= 1;\n      k += sizes[i];\n   }\n   z->maxcode[16] = 0x10000; // sentinel\n   for (i=0; i < num; ++i) {\n      int s = sizelist[i];\n      if (s) {\n         int c = next_code[s] - z->firstcode[s] + z->firstsymbol[s];\n         stbi__uint16 fastv = (stbi__uint16) ((s << 9) | i);\n         z->size [c] = (stbi_uc     ) s;\n         z->value[c] = (stbi__uint16) i;\n         if (s <= STBI__ZFAST_BITS) {\n            int j = stbi__bit_reverse(next_code[s],s);\n            while (j < (1 << STBI__ZFAST_BITS)) {\n               z->fast[j] = fastv;\n               j += (1 << s);\n            }\n         }\n         ++next_code[s];\n      }\n   }\n   return 1;\n}\n\n// zlib-from-memory implementation for PNG reading\n//    because PNG allows splitting the zlib stream arbitrarily,\n//    and it's annoying structurally to have PNG call ZLIB call PNG,\n//    we require PNG read all the IDATs and combine them into a single\n//    memory buffer\n\ntypedef struct\n{\n   stbi_uc *zbuffer, *zbuffer_end;\n   int num_bits;\n   stbi__uint32 code_buffer;\n\n   char *zout;\n   char *zout_start;\n   char *zout_end;\n   int   z_expandable;\n\n   stbi__zhuffman z_length, z_distance;\n} stbi__zbuf;\n\nstbi_inline static stbi_uc stbi__zget8(stbi__zbuf *z)\n{\n   if (z->zbuffer >= z->zbuffer_end) return 0;\n   return *z->zbuffer++;\n}\n\nstatic void stbi__fill_bits(stbi__zbuf *z)\n{\n   do {\n      STBI_ASSERT(z->code_buffer < (1U << z->num_bits));\n      z->code_buffer |= (unsigned int) stbi__zget8(z) << z->num_bits;\n      z->num_bits += 8;\n   } while (z->num_bits <= 24);\n}\n\nstbi_inline static unsigned int stbi__zreceive(stbi__zbuf *z, int n)\n{\n   unsigned int k;\n   if (z->num_bits < n) stbi__fill_bits(z);\n   k = z->code_buffer & ((1 << n) - 1);\n   z->code_buffer >>= n;\n   z->num_bits -= n;\n   return k;\n}\n\nstatic int stbi__zhuffman_decode_slowpath(stbi__zbuf *a, stbi__zhuffman *z)\n{\n   int b,s,k;\n   // not resolved by fast table, so compute it the slow way\n   // use jpeg approach, which requires MSbits at top\n   k = stbi__bit_reverse(a->code_buffer, 16);\n   for (s=STBI__ZFAST_BITS+1; ; ++s)\n      if (k < z->maxcode[s])\n         break;\n   if (s == 16) return -1; // invalid code!\n   // code size is s, so:\n   b = (k >> (16-s)) - z->firstcode[s] + z->firstsymbol[s];\n   STBI_ASSERT(z->size[b] == s);\n   a->code_buffer >>= s;\n   a->num_bits -= s;\n   return z->value[b];\n}\n\nstbi_inline static int stbi__zhuffman_decode(stbi__zbuf *a, stbi__zhuffman *z)\n{\n   int b,s;\n   if (a->num_bits < 16) stbi__fill_bits(a);\n   b = z->fast[a->code_buffer & STBI__ZFAST_MASK];\n   if (b) {\n      s = b >> 9;\n      a->code_buffer >>= s;\n      a->num_bits -= s;\n      return b & 511;\n   }\n   return stbi__zhuffman_decode_slowpath(a, z);\n}\n\nstatic int stbi__zexpand(stbi__zbuf *z, char *zout, int n)  // need to make room for n bytes\n{\n   char *q;\n   int cur, limit, old_limit;\n   z->zout = zout;\n   if (!z->z_expandable) return stbi__err(\"output buffer limit\",\"Corrupt PNG\");\n   cur   = (int) (z->zout     - z->zout_start);\n   limit = old_limit = (int) (z->zout_end - z->zout_start);\n   while (cur + n > limit)\n      limit *= 2;\n   q = (char *) STBI_REALLOC_SIZED(z->zout_start, old_limit, limit);\n   STBI_NOTUSED(old_limit);\n   if (q == NULL) return stbi__err(\"outofmem\", \"Out of memory\");\n   z->zout_start = q;\n   z->zout       = q + cur;\n   z->zout_end   = q + limit;\n   return 1;\n}\n\nstatic const int stbi__zlength_base[31] = {\n   3,4,5,6,7,8,9,10,11,13,\n   15,17,19,23,27,31,35,43,51,59,\n   67,83,99,115,131,163,195,227,258,0,0 };\n\nstatic const int stbi__zlength_extra[31]=\n{ 0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0,0,0 };\n\nstatic const int stbi__zdist_base[32] = { 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193,\n257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577,0,0};\n\nstatic const int stbi__zdist_extra[32] =\n{ 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13};\n\nstatic int stbi__parse_huffman_block(stbi__zbuf *a)\n{\n   char *zout = a->zout;\n   for(;;) {\n      int z = stbi__zhuffman_decode(a, &a->z_length);\n      if (z < 256) {\n         if (z < 0) return stbi__err(\"bad huffman code\",\"Corrupt PNG\"); // error in huffman codes\n         if (zout >= a->zout_end) {\n            if (!stbi__zexpand(a, zout, 1)) return 0;\n            zout = a->zout;\n         }\n         *zout++ = (char) z;\n      } else {\n         stbi_uc *p;\n         int len,dist;\n         if (z == 256) {\n            a->zout = zout;\n            return 1;\n         }\n         z -= 257;\n         len = stbi__zlength_base[z];\n         if (stbi__zlength_extra[z]) len += stbi__zreceive(a, stbi__zlength_extra[z]);\n         z = stbi__zhuffman_decode(a, &a->z_distance);\n         if (z < 0) return stbi__err(\"bad huffman code\",\"Corrupt PNG\");\n         dist = stbi__zdist_base[z];\n         if (stbi__zdist_extra[z]) dist += stbi__zreceive(a, stbi__zdist_extra[z]);\n         if (zout - a->zout_start < dist) return stbi__err(\"bad dist\",\"Corrupt PNG\");\n         if (zout + len > a->zout_end) {\n            if (!stbi__zexpand(a, zout, len)) return 0;\n            zout = a->zout;\n         }\n         p = (stbi_uc *) (zout - dist);\n         if (dist == 1) { // run of one byte; common in images.\n            stbi_uc v = *p;\n            if (len) { do *zout++ = v; while (--len); }\n         } else {\n            if (len) { do *zout++ = *p++; while (--len); }\n         }\n      }\n   }\n}\n\nstatic int stbi__compute_huffman_codes(stbi__zbuf *a)\n{\n   static const stbi_uc length_dezigzag[19] = { 16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15 };\n   stbi__zhuffman z_codelength;\n   stbi_uc lencodes[286+32+137];//padding for maximum single op\n   stbi_uc codelength_sizes[19];\n   int i,n;\n\n   int hlit  = stbi__zreceive(a,5) + 257;\n   int hdist = stbi__zreceive(a,5) + 1;\n   int hclen = stbi__zreceive(a,4) + 4;\n   int ntot  = hlit + hdist;\n\n   memset(codelength_sizes, 0, sizeof(codelength_sizes));\n   for (i=0; i < hclen; ++i) {\n      int s = stbi__zreceive(a,3);\n      codelength_sizes[length_dezigzag[i]] = (stbi_uc) s;\n   }\n   if (!stbi__zbuild_huffman(&z_codelength, codelength_sizes, 19)) return 0;\n\n   n = 0;\n   while (n < ntot) {\n      int c = stbi__zhuffman_decode(a, &z_codelength);\n      if (c < 0 || c >= 19) return stbi__err(\"bad codelengths\", \"Corrupt PNG\");\n      if (c < 16)\n         lencodes[n++] = (stbi_uc) c;\n      else {\n         stbi_uc fill = 0;\n         if (c == 16) {\n            c = stbi__zreceive(a,2)+3;\n            if (n == 0) return stbi__err(\"bad codelengths\", \"Corrupt PNG\");\n            fill = lencodes[n-1];\n         } else if (c == 17)\n            c = stbi__zreceive(a,3)+3;\n         else {\n            STBI_ASSERT(c == 18);\n            c = stbi__zreceive(a,7)+11;\n         }\n         if (ntot - n < c) return stbi__err(\"bad codelengths\", \"Corrupt PNG\");\n         memset(lencodes+n, fill, c);\n         n += c;\n      }\n   }\n   if (n != ntot) return stbi__err(\"bad codelengths\",\"Corrupt PNG\");\n   if (!stbi__zbuild_huffman(&a->z_length, lencodes, hlit)) return 0;\n   if (!stbi__zbuild_huffman(&a->z_distance, lencodes+hlit, hdist)) return 0;\n   return 1;\n}\n\nstatic int stbi__parse_uncompressed_block(stbi__zbuf *a)\n{\n   stbi_uc header[4];\n   int len,nlen,k;\n   if (a->num_bits & 7)\n      stbi__zreceive(a, a->num_bits & 7); // discard\n   // drain the bit-packed data into header\n   k = 0;\n   while (a->num_bits > 0) {\n      header[k++] = (stbi_uc) (a->code_buffer & 255); // suppress MSVC run-time check\n      a->code_buffer >>= 8;\n      a->num_bits -= 8;\n   }\n   STBI_ASSERT(a->num_bits == 0);\n   // now fill header the normal way\n   while (k < 4)\n      header[k++] = stbi__zget8(a);\n   len  = header[1] * 256 + header[0];\n   nlen = header[3] * 256 + header[2];\n   if (nlen != (len ^ 0xffff)) return stbi__err(\"zlib corrupt\",\"Corrupt PNG\");\n   if (a->zbuffer + len > a->zbuffer_end) return stbi__err(\"read past buffer\",\"Corrupt PNG\");\n   if (a->zout + len > a->zout_end)\n      if (!stbi__zexpand(a, a->zout, len)) return 0;\n   memcpy(a->zout, a->zbuffer, len);\n   a->zbuffer += len;\n   a->zout += len;\n   return 1;\n}\n\nstatic int stbi__parse_zlib_header(stbi__zbuf *a)\n{\n   int cmf   = stbi__zget8(a);\n   int cm    = cmf & 15;\n   /* int cinfo = cmf >> 4; */\n   int flg   = stbi__zget8(a);\n   if ((cmf*256+flg) % 31 != 0) return stbi__err(\"bad zlib header\",\"Corrupt PNG\"); // zlib spec\n   if (flg & 32) return stbi__err(\"no preset dict\",\"Corrupt PNG\"); // preset dictionary not allowed in png\n   if (cm != 8) return stbi__err(\"bad compression\",\"Corrupt PNG\"); // DEFLATE required for png\n   // window = 1 << (8 + cinfo)... but who cares, we fully buffer output\n   return 1;\n}\n\nstatic const stbi_uc stbi__zdefault_length[288] =\n{\n   8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,\n   8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,\n   8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,\n   8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,\n   8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,\n   9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,\n   9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,\n   9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,\n   7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 7,7,7,7,7,7,7,7,8,8,8,8,8,8,8,8\n};\nstatic const stbi_uc stbi__zdefault_distance[32] =\n{\n   5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5\n};\n/*\nInit algorithm:\n{\n   int i;   // use <= to match clearly with spec\n   for (i=0; i <= 143; ++i)     stbi__zdefault_length[i]   = 8;\n   for (   ; i <= 255; ++i)     stbi__zdefault_length[i]   = 9;\n   for (   ; i <= 279; ++i)     stbi__zdefault_length[i]   = 7;\n   for (   ; i <= 287; ++i)     stbi__zdefault_length[i]   = 8;\n\n   for (i=0; i <=  31; ++i)     stbi__zdefault_distance[i] = 5;\n}\n*/\n\nstatic int stbi__parse_zlib(stbi__zbuf *a, int parse_header)\n{\n   int final, type;\n   if (parse_header)\n      if (!stbi__parse_zlib_header(a)) return 0;\n   a->num_bits = 0;\n   a->code_buffer = 0;\n   do {\n      final = stbi__zreceive(a,1);\n      type = stbi__zreceive(a,2);\n      if (type == 0) {\n         if (!stbi__parse_uncompressed_block(a)) return 0;\n      } else if (type == 3) {\n         return 0;\n      } else {\n         if (type == 1) {\n            // use fixed code lengths\n            if (!stbi__zbuild_huffman(&a->z_length  , stbi__zdefault_length  , 288)) return 0;\n            if (!stbi__zbuild_huffman(&a->z_distance, stbi__zdefault_distance,  32)) return 0;\n         } else {\n            if (!stbi__compute_huffman_codes(a)) return 0;\n         }\n         if (!stbi__parse_huffman_block(a)) return 0;\n      }\n   } while (!final);\n   return 1;\n}\n\nstatic int stbi__do_zlib(stbi__zbuf *a, char *obuf, int olen, int exp, int parse_header)\n{\n   a->zout_start = obuf;\n   a->zout       = obuf;\n   a->zout_end   = obuf + olen;\n   a->z_expandable = exp;\n\n   return stbi__parse_zlib(a, parse_header);\n}\n\nSTBIDEF char *stbi_zlib_decode_malloc_guesssize(const char *buffer, int len, int initial_size, int *outlen)\n{\n   stbi__zbuf a;\n   char *p = (char *) stbi__malloc(initial_size);\n   if (p == NULL) return NULL;\n   a.zbuffer = (stbi_uc *) buffer;\n   a.zbuffer_end = (stbi_uc *) buffer + len;\n   if (stbi__do_zlib(&a, p, initial_size, 1, 1)) {\n      if (outlen) *outlen = (int) (a.zout - a.zout_start);\n      return a.zout_start;\n   } else {\n      STBI_FREE(a.zout_start);\n      return NULL;\n   }\n}\n\nSTBIDEF char *stbi_zlib_decode_malloc(char const *buffer, int len, int *outlen)\n{\n   return stbi_zlib_decode_malloc_guesssize(buffer, len, 16384, outlen);\n}\n\nSTBIDEF char *stbi_zlib_decode_malloc_guesssize_headerflag(const char *buffer, int len, int initial_size, int *outlen, int parse_header)\n{\n   stbi__zbuf a;\n   char *p = (char *) stbi__malloc(initial_size);\n   if (p == NULL) return NULL;\n   a.zbuffer = (stbi_uc *) buffer;\n   a.zbuffer_end = (stbi_uc *) buffer + len;\n   if (stbi__do_zlib(&a, p, initial_size, 1, parse_header)) {\n      if (outlen) *outlen = (int) (a.zout - a.zout_start);\n      return a.zout_start;\n   } else {\n      STBI_FREE(a.zout_start);\n      return NULL;\n   }\n}\n\nSTBIDEF int stbi_zlib_decode_buffer(char *obuffer, int olen, char const *ibuffer, int ilen)\n{\n   stbi__zbuf a;\n   a.zbuffer = (stbi_uc *) ibuffer;\n   a.zbuffer_end = (stbi_uc *) ibuffer + ilen;\n   if (stbi__do_zlib(&a, obuffer, olen, 0, 1))\n      return (int) (a.zout - a.zout_start);\n   else\n      return -1;\n}\n\nSTBIDEF char *stbi_zlib_decode_noheader_malloc(char const *buffer, int len, int *outlen)\n{\n   stbi__zbuf a;\n   char *p = (char *) stbi__malloc(16384);\n   if (p == NULL) return NULL;\n   a.zbuffer = (stbi_uc *) buffer;\n   a.zbuffer_end = (stbi_uc *) buffer+len;\n   if (stbi__do_zlib(&a, p, 16384, 1, 0)) {\n      if (outlen) *outlen = (int) (a.zout - a.zout_start);\n      return a.zout_start;\n   } else {\n      STBI_FREE(a.zout_start);\n      return NULL;\n   }\n}\n\nSTBIDEF int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const char *ibuffer, int ilen)\n{\n   stbi__zbuf a;\n   a.zbuffer = (stbi_uc *) ibuffer;\n   a.zbuffer_end = (stbi_uc *) ibuffer + ilen;\n   if (stbi__do_zlib(&a, obuffer, olen, 0, 0))\n      return (int) (a.zout - a.zout_start);\n   else\n      return -1;\n}\n#endif\n\n// public domain \"baseline\" PNG decoder   v0.10  Sean Barrett 2006-11-18\n//    simple implementation\n//      - only 8-bit samples\n//      - no CRC checking\n//      - allocates lots of intermediate memory\n//        - avoids problem of streaming data between subsystems\n//        - avoids explicit window management\n//    performance\n//      - uses stb_zlib, a PD zlib implementation with fast huffman decoding\n\ntypedef struct\n{\n   stbi__uint32 length;\n   stbi__uint32 type;\n} stbi__pngchunk;\n\nstatic stbi__pngchunk stbi__get_chunk_header(stbi__context *s)\n{\n   stbi__pngchunk c;\n   c.length = stbi__get32be(s);\n   c.type   = stbi__get32be(s);\n   return c;\n}\n\nstatic int stbi__check_png_header(stbi__context *s)\n{\n   static const stbi_uc png_sig[8] = { 137,80,78,71,13,10,26,10 };\n   int i;\n   for (i=0; i < 8; ++i)\n      if (stbi__get8(s) != png_sig[i]) return stbi__err(\"bad png sig\",\"Not a PNG\");\n   return 1;\n}\n\ntypedef struct\n{\n   stbi__context *s;\n   stbi_uc *idata, *expanded, *out;\n   int depth;\n} stbi__png;\n\n\nenum {\n   STBI__F_none=0,\n   STBI__F_sub=1,\n   STBI__F_up=2,\n   STBI__F_avg=3,\n   STBI__F_paeth=4,\n   // synthetic filters used for first scanline to avoid needing a dummy row of 0s\n   STBI__F_avg_first,\n   STBI__F_paeth_first\n};\n\nstatic stbi_uc first_row_filter[5] =\n{\n   STBI__F_none,\n   STBI__F_sub,\n   STBI__F_none,\n   STBI__F_avg_first,\n   STBI__F_paeth_first\n};\n\nstatic int stbi__paeth(int a, int b, int c)\n{\n   int p = a + b - c;\n   int pa = abs(p-a);\n   int pb = abs(p-b);\n   int pc = abs(p-c);\n   if (pa <= pb && pa <= pc) return a;\n   if (pb <= pc) return b;\n   return c;\n}\n\nstatic const stbi_uc stbi__depth_scale_table[9] = { 0, 0xff, 0x55, 0, 0x11, 0,0,0, 0x01 };\n\n// create the png data from post-deflated data\nstatic int stbi__create_png_image_raw(stbi__png *a, stbi_uc *raw, stbi__uint32 raw_len, int out_n, stbi__uint32 x, stbi__uint32 y, int depth, int color)\n{\n   int bytes = (depth == 16? 2 : 1);\n   stbi__context *s = a->s;\n   stbi__uint32 i,j,stride = x*out_n*bytes;\n   stbi__uint32 img_len, img_width_bytes;\n   int k;\n   int img_n = s->img_n; // copy it into a local for later\n\n   int output_bytes = out_n*bytes;\n   int filter_bytes = img_n*bytes;\n   int width = x;\n\n   STBI_ASSERT(out_n == s->img_n || out_n == s->img_n+1);\n   a->out = (stbi_uc *) stbi__malloc_mad3(x, y, output_bytes, 0); // extra bytes to write off the end into\n   if (!a->out) return stbi__err(\"outofmem\", \"Out of memory\");\n\n   if (!stbi__mad3sizes_valid(img_n, x, depth, 7)) return stbi__err(\"too large\", \"Corrupt PNG\");\n   img_width_bytes = (((img_n * x * depth) + 7) >> 3);\n   img_len = (img_width_bytes + 1) * y;\n\n   // we used to check for exact match between raw_len and img_len on non-interlaced PNGs,\n   // but issue #276 reported a PNG in the wild that had extra data at the end (all zeros),\n   // so just check for raw_len < img_len always.\n   if (raw_len < img_len) return stbi__err(\"not enough pixels\",\"Corrupt PNG\");\n\n   for (j=0; j < y; ++j) {\n      stbi_uc *cur = a->out + stride*j;\n      stbi_uc *prior;\n      int filter = *raw++;\n\n      if (filter > 4)\n         return stbi__err(\"invalid filter\",\"Corrupt PNG\");\n\n      if (depth < 8) {\n         STBI_ASSERT(img_width_bytes <= x);\n         cur += x*out_n - img_width_bytes; // store output to the rightmost img_len bytes, so we can decode in place\n         filter_bytes = 1;\n         width = img_width_bytes;\n      }\n      prior = cur - stride; // bugfix: need to compute this after 'cur +=' computation above\n\n      // if first row, use special filter that doesn't sample previous row\n      if (j == 0) filter = first_row_filter[filter];\n\n      // handle first byte explicitly\n      for (k=0; k < filter_bytes; ++k) {\n         switch (filter) {\n            case STBI__F_none       : cur[k] = raw[k]; break;\n            case STBI__F_sub        : cur[k] = raw[k]; break;\n            case STBI__F_up         : cur[k] = STBI__BYTECAST(raw[k] + prior[k]); break;\n            case STBI__F_avg        : cur[k] = STBI__BYTECAST(raw[k] + (prior[k]>>1)); break;\n            case STBI__F_paeth      : cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(0,prior[k],0)); break;\n            case STBI__F_avg_first  : cur[k] = raw[k]; break;\n            case STBI__F_paeth_first: cur[k] = raw[k]; break;\n         }\n      }\n\n      if (depth == 8) {\n         if (img_n != out_n)\n            cur[img_n] = 255; // first pixel\n         raw += img_n;\n         cur += out_n;\n         prior += out_n;\n      } else if (depth == 16) {\n         if (img_n != out_n) {\n            cur[filter_bytes]   = 255; // first pixel top byte\n            cur[filter_bytes+1] = 255; // first pixel bottom byte\n         }\n         raw += filter_bytes;\n         cur += output_bytes;\n         prior += output_bytes;\n      } else {\n         raw += 1;\n         cur += 1;\n         prior += 1;\n      }\n\n      // this is a little gross, so that we don't switch per-pixel or per-component\n      if (depth < 8 || img_n == out_n) {\n         int nk = (width - 1)*filter_bytes;\n         #define STBI__CASE(f) \\\n             case f:     \\\n                for (k=0; k < nk; ++k)\n         switch (filter) {\n            // \"none\" filter turns into a memcpy here; make that explicit.\n            case STBI__F_none:         memcpy(cur, raw, nk); break;\n            STBI__CASE(STBI__F_sub)          { cur[k] = STBI__BYTECAST(raw[k] + cur[k-filter_bytes]); } break;\n            STBI__CASE(STBI__F_up)           { cur[k] = STBI__BYTECAST(raw[k] + prior[k]); } break;\n            STBI__CASE(STBI__F_avg)          { cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k-filter_bytes])>>1)); } break;\n            STBI__CASE(STBI__F_paeth)        { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes],prior[k],prior[k-filter_bytes])); } break;\n            STBI__CASE(STBI__F_avg_first)    { cur[k] = STBI__BYTECAST(raw[k] + (cur[k-filter_bytes] >> 1)); } break;\n            STBI__CASE(STBI__F_paeth_first)  { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes],0,0)); } break;\n         }\n         #undef STBI__CASE\n         raw += nk;\n      } else {\n         STBI_ASSERT(img_n+1 == out_n);\n         #define STBI__CASE(f) \\\n             case f:     \\\n                for (i=x-1; i >= 1; --i, cur[filter_bytes]=255,raw+=filter_bytes,cur+=output_bytes,prior+=output_bytes) \\\n                   for (k=0; k < filter_bytes; ++k)\n         switch (filter) {\n            STBI__CASE(STBI__F_none)         { cur[k] = raw[k]; } break;\n            STBI__CASE(STBI__F_sub)          { cur[k] = STBI__BYTECAST(raw[k] + cur[k- output_bytes]); } break;\n            STBI__CASE(STBI__F_up)           { cur[k] = STBI__BYTECAST(raw[k] + prior[k]); } break;\n            STBI__CASE(STBI__F_avg)          { cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k- output_bytes])>>1)); } break;\n            STBI__CASE(STBI__F_paeth)        { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k- output_bytes],prior[k],prior[k- output_bytes])); } break;\n            STBI__CASE(STBI__F_avg_first)    { cur[k] = STBI__BYTECAST(raw[k] + (cur[k- output_bytes] >> 1)); } break;\n            STBI__CASE(STBI__F_paeth_first)  { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k- output_bytes],0,0)); } break;\n         }\n         #undef STBI__CASE\n\n         // the loop above sets the high byte of the pixels' alpha, but for\n         // 16 bit png files we also need the low byte set. we'll do that here.\n         if (depth == 16) {\n            cur = a->out + stride*j; // start at the beginning of the row again\n            for (i=0; i < x; ++i,cur+=output_bytes) {\n               cur[filter_bytes+1] = 255;\n            }\n         }\n      }\n   }\n\n   // we make a separate pass to expand bits to pixels; for performance,\n   // this could run two scanlines behind the above code, so it won't\n   // intefere with filtering but will still be in the cache.\n   if (depth < 8) {\n      for (j=0; j < y; ++j) {\n         stbi_uc *cur = a->out + stride*j;\n         stbi_uc *in  = a->out + stride*j + x*out_n - img_width_bytes;\n         // unpack 1/2/4-bit into a 8-bit buffer. allows us to keep the common 8-bit path optimal at minimal cost for 1/2/4-bit\n         // png guarante byte alignment, if width is not multiple of 8/4/2 we'll decode dummy trailing data that will be skipped in the later loop\n         stbi_uc scale = (color == 0) ? stbi__depth_scale_table[depth] : 1; // scale grayscale values to 0..255 range\n\n         // note that the final byte might overshoot and write more data than desired.\n         // we can allocate enough data that this never writes out of memory, but it\n         // could also overwrite the next scanline. can it overwrite non-empty data\n         // on the next scanline? yes, consider 1-pixel-wide scanlines with 1-bit-per-pixel.\n         // so we need to explicitly clamp the final ones\n\n         if (depth == 4) {\n            for (k=x*img_n; k >= 2; k-=2, ++in) {\n               *cur++ = scale * ((*in >> 4)       );\n               *cur++ = scale * ((*in     ) & 0x0f);\n            }\n            if (k > 0) *cur++ = scale * ((*in >> 4)       );\n         } else if (depth == 2) {\n            for (k=x*img_n; k >= 4; k-=4, ++in) {\n               *cur++ = scale * ((*in >> 6)       );\n               *cur++ = scale * ((*in >> 4) & 0x03);\n               *cur++ = scale * ((*in >> 2) & 0x03);\n               *cur++ = scale * ((*in     ) & 0x03);\n            }\n            if (k > 0) *cur++ = scale * ((*in >> 6)       );\n            if (k > 1) *cur++ = scale * ((*in >> 4) & 0x03);\n            if (k > 2) *cur++ = scale * ((*in >> 2) & 0x03);\n         } else if (depth == 1) {\n            for (k=x*img_n; k >= 8; k-=8, ++in) {\n               *cur++ = scale * ((*in >> 7)       );\n               *cur++ = scale * ((*in >> 6) & 0x01);\n               *cur++ = scale * ((*in >> 5) & 0x01);\n               *cur++ = scale * ((*in >> 4) & 0x01);\n               *cur++ = scale * ((*in >> 3) & 0x01);\n               *cur++ = scale * ((*in >> 2) & 0x01);\n               *cur++ = scale * ((*in >> 1) & 0x01);\n               *cur++ = scale * ((*in     ) & 0x01);\n            }\n            if (k > 0) *cur++ = scale * ((*in >> 7)       );\n            if (k > 1) *cur++ = scale * ((*in >> 6) & 0x01);\n            if (k > 2) *cur++ = scale * ((*in >> 5) & 0x01);\n            if (k > 3) *cur++ = scale * ((*in >> 4) & 0x01);\n            if (k > 4) *cur++ = scale * ((*in >> 3) & 0x01);\n            if (k > 5) *cur++ = scale * ((*in >> 2) & 0x01);\n            if (k > 6) *cur++ = scale * ((*in >> 1) & 0x01);\n         }\n         if (img_n != out_n) {\n            int q;\n            // insert alpha = 255\n            cur = a->out + stride*j;\n            if (img_n == 1) {\n               for (q=x-1; q >= 0; --q) {\n                  cur[q*2+1] = 255;\n                  cur[q*2+0] = cur[q];\n               }\n            } else {\n               STBI_ASSERT(img_n == 3);\n               for (q=x-1; q >= 0; --q) {\n                  cur[q*4+3] = 255;\n                  cur[q*4+2] = cur[q*3+2];\n                  cur[q*4+1] = cur[q*3+1];\n                  cur[q*4+0] = cur[q*3+0];\n               }\n            }\n         }\n      }\n   } else if (depth == 16) {\n      // force the image data from big-endian to platform-native.\n      // this is done in a separate pass due to the decoding relying\n      // on the data being untouched, but could probably be done\n      // per-line during decode if care is taken.\n      stbi_uc *cur = a->out;\n      stbi__uint16 *cur16 = (stbi__uint16*)cur;\n\n      for(i=0; i < x*y*out_n; ++i,cur16++,cur+=2) {\n         *cur16 = (cur[0] << 8) | cur[1];\n      }\n   }\n\n   return 1;\n}\n\nstatic int stbi__create_png_image(stbi__png *a, stbi_uc *image_data, stbi__uint32 image_data_len, int out_n, int depth, int color, int interlaced)\n{\n   int bytes = (depth == 16 ? 2 : 1);\n   int out_bytes = out_n * bytes;\n   stbi_uc *final;\n   int p;\n   if (!interlaced)\n      return stbi__create_png_image_raw(a, image_data, image_data_len, out_n, a->s->img_x, a->s->img_y, depth, color);\n\n   // de-interlacing\n   final = (stbi_uc *) stbi__malloc_mad3(a->s->img_x, a->s->img_y, out_bytes, 0);\n   for (p=0; p < 7; ++p) {\n      int xorig[] = { 0,4,0,2,0,1,0 };\n      int yorig[] = { 0,0,4,0,2,0,1 };\n      int xspc[]  = { 8,8,4,4,2,2,1 };\n      int yspc[]  = { 8,8,8,4,4,2,2 };\n      int i,j,x,y;\n      // pass1_x[4] = 0, pass1_x[5] = 1, pass1_x[12] = 1\n      x = (a->s->img_x - xorig[p] + xspc[p]-1) / xspc[p];\n      y = (a->s->img_y - yorig[p] + yspc[p]-1) / yspc[p];\n      if (x && y) {\n         stbi__uint32 img_len = ((((a->s->img_n * x * depth) + 7) >> 3) + 1) * y;\n         if (!stbi__create_png_image_raw(a, image_data, image_data_len, out_n, x, y, depth, color)) {\n            STBI_FREE(final);\n            return 0;\n         }\n         for (j=0; j < y; ++j) {\n            for (i=0; i < x; ++i) {\n               int out_y = j*yspc[p]+yorig[p];\n               int out_x = i*xspc[p]+xorig[p];\n               memcpy(final + out_y*a->s->img_x*out_bytes + out_x*out_bytes,\n                      a->out + (j*x+i)*out_bytes, out_bytes);\n            }\n         }\n         STBI_FREE(a->out);\n         image_data += img_len;\n         image_data_len -= img_len;\n      }\n   }\n   a->out = final;\n\n   return 1;\n}\n\nstatic int stbi__compute_transparency(stbi__png *z, stbi_uc tc[3], int out_n)\n{\n   stbi__context *s = z->s;\n   stbi__uint32 i, pixel_count = s->img_x * s->img_y;\n   stbi_uc *p = z->out;\n\n   // compute color-based transparency, assuming we've\n   // already got 255 as the alpha value in the output\n   STBI_ASSERT(out_n == 2 || out_n == 4);\n\n   if (out_n == 2) {\n      for (i=0; i < pixel_count; ++i) {\n         p[1] = (p[0] == tc[0] ? 0 : 255);\n         p += 2;\n      }\n   } else {\n      for (i=0; i < pixel_count; ++i) {\n         if (p[0] == tc[0] && p[1] == tc[1] && p[2] == tc[2])\n            p[3] = 0;\n         p += 4;\n      }\n   }\n   return 1;\n}\n\nstatic int stbi__compute_transparency16(stbi__png *z, stbi__uint16 tc[3], int out_n)\n{\n   stbi__context *s = z->s;\n   stbi__uint32 i, pixel_count = s->img_x * s->img_y;\n   stbi__uint16 *p = (stbi__uint16*) z->out;\n\n   // compute color-based transparency, assuming we've\n   // already got 65535 as the alpha value in the output\n   STBI_ASSERT(out_n == 2 || out_n == 4);\n\n   if (out_n == 2) {\n      for (i = 0; i < pixel_count; ++i) {\n         p[1] = (p[0] == tc[0] ? 0 : 65535);\n         p += 2;\n      }\n   } else {\n      for (i = 0; i < pixel_count; ++i) {\n         if (p[0] == tc[0] && p[1] == tc[1] && p[2] == tc[2])\n            p[3] = 0;\n         p += 4;\n      }\n   }\n   return 1;\n}\n\nstatic int stbi__expand_png_palette(stbi__png *a, stbi_uc *palette, int len, int pal_img_n)\n{\n   stbi__uint32 i, pixel_count = a->s->img_x * a->s->img_y;\n   stbi_uc *p, *temp_out, *orig = a->out;\n\n   p = (stbi_uc *) stbi__malloc_mad2(pixel_count, pal_img_n, 0);\n   if (p == NULL) return stbi__err(\"outofmem\", \"Out of memory\");\n\n   // between here and free(out) below, exitting would leak\n   temp_out = p;\n\n   if (pal_img_n == 3) {\n      for (i=0; i < pixel_count; ++i) {\n         int n = orig[i]*4;\n         p[0] = palette[n  ];\n         p[1] = palette[n+1];\n         p[2] = palette[n+2];\n         p += 3;\n      }\n   } else {\n      for (i=0; i < pixel_count; ++i) {\n         int n = orig[i]*4;\n         p[0] = palette[n  ];\n         p[1] = palette[n+1];\n         p[2] = palette[n+2];\n         p[3] = palette[n+3];\n         p += 4;\n      }\n   }\n   STBI_FREE(a->out);\n   a->out = temp_out;\n\n   STBI_NOTUSED(len);\n\n   return 1;\n}\n\nstatic int stbi__unpremultiply_on_load = 0;\nstatic int stbi__de_iphone_flag = 0;\n\nSTBIDEF void stbi_set_unpremultiply_on_load(int flag_true_if_should_unpremultiply)\n{\n   stbi__unpremultiply_on_load = flag_true_if_should_unpremultiply;\n}\n\nSTBIDEF void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert)\n{\n   stbi__de_iphone_flag = flag_true_if_should_convert;\n}\n\nstatic void stbi__de_iphone(stbi__png *z)\n{\n   stbi__context *s = z->s;\n   stbi__uint32 i, pixel_count = s->img_x * s->img_y;\n   stbi_uc *p = z->out;\n\n   if (s->img_out_n == 3) {  // convert bgr to rgb\n      for (i=0; i < pixel_count; ++i) {\n         stbi_uc t = p[0];\n         p[0] = p[2];\n         p[2] = t;\n         p += 3;\n      }\n   } else {\n      STBI_ASSERT(s->img_out_n == 4);\n      if (stbi__unpremultiply_on_load) {\n         // convert bgr to rgb and unpremultiply\n         for (i=0; i < pixel_count; ++i) {\n            stbi_uc a = p[3];\n            stbi_uc t = p[0];\n            if (a) {\n               stbi_uc half = a / 2;\n               p[0] = (p[2] * 255 + half) / a;\n               p[1] = (p[1] * 255 + half) / a;\n               p[2] = ( t   * 255 + half) / a;\n            } else {\n               p[0] = p[2];\n               p[2] = t;\n            }\n            p += 4;\n         }\n      } else {\n         // convert bgr to rgb\n         for (i=0; i < pixel_count; ++i) {\n            stbi_uc t = p[0];\n            p[0] = p[2];\n            p[2] = t;\n            p += 4;\n         }\n      }\n   }\n}\n\n#define STBI__PNG_TYPE(a,b,c,d)  (((unsigned) (a) << 24) + ((unsigned) (b) << 16) + ((unsigned) (c) << 8) + (unsigned) (d))\n\nstatic int stbi__parse_png_file(stbi__png *z, int scan, int req_comp)\n{\n   stbi_uc palette[1024], pal_img_n=0;\n   stbi_uc has_trans=0, tc[3];\n   stbi__uint16 tc16[3];\n   stbi__uint32 ioff=0, idata_limit=0, i, pal_len=0;\n   int first=1,k,interlace=0, color=0, is_iphone=0;\n   stbi__context *s = z->s;\n\n   z->expanded = NULL;\n   z->idata = NULL;\n   z->out = NULL;\n\n   if (!stbi__check_png_header(s)) return 0;\n\n   if (scan == STBI__SCAN_type) return 1;\n\n   for (;;) {\n      stbi__pngchunk c = stbi__get_chunk_header(s);\n      switch (c.type) {\n         case STBI__PNG_TYPE('C','g','B','I'):\n            is_iphone = 1;\n            stbi__skip(s, c.length);\n            break;\n         case STBI__PNG_TYPE('I','H','D','R'): {\n            int comp,filter;\n            if (!first) return stbi__err(\"multiple IHDR\",\"Corrupt PNG\");\n            first = 0;\n            if (c.length != 13) return stbi__err(\"bad IHDR len\",\"Corrupt PNG\");\n            s->img_x = stbi__get32be(s); if (s->img_x > (1 << 24)) return stbi__err(\"too large\",\"Very large image (corrupt?)\");\n            s->img_y = stbi__get32be(s); if (s->img_y > (1 << 24)) return stbi__err(\"too large\",\"Very large image (corrupt?)\");\n            z->depth = stbi__get8(s);  if (z->depth != 1 && z->depth != 2 && z->depth != 4 && z->depth != 8 && z->depth != 16)  return stbi__err(\"1/2/4/8/16-bit only\",\"PNG not supported: 1/2/4/8/16-bit only\");\n            color = stbi__get8(s);  if (color > 6)         return stbi__err(\"bad ctype\",\"Corrupt PNG\");\n            if (color == 3 && z->depth == 16)                  return stbi__err(\"bad ctype\",\"Corrupt PNG\");\n            if (color == 3) pal_img_n = 3; else if (color & 1) return stbi__err(\"bad ctype\",\"Corrupt PNG\");\n            comp  = stbi__get8(s);  if (comp) return stbi__err(\"bad comp method\",\"Corrupt PNG\");\n            filter= stbi__get8(s);  if (filter) return stbi__err(\"bad filter method\",\"Corrupt PNG\");\n            interlace = stbi__get8(s); if (interlace>1) return stbi__err(\"bad interlace method\",\"Corrupt PNG\");\n            if (!s->img_x || !s->img_y) return stbi__err(\"0-pixel image\",\"Corrupt PNG\");\n            if (!pal_img_n) {\n               s->img_n = (color & 2 ? 3 : 1) + (color & 4 ? 1 : 0);\n               if ((1 << 30) / s->img_x / s->img_n < s->img_y) return stbi__err(\"too large\", \"Image too large to decode\");\n               if (scan == STBI__SCAN_header) return 1;\n            } else {\n               // if paletted, then pal_n is our final components, and\n               // img_n is # components to decompress/filter.\n               s->img_n = 1;\n               if ((1 << 30) / s->img_x / 4 < s->img_y) return stbi__err(\"too large\",\"Corrupt PNG\");\n               // if SCAN_header, have to scan to see if we have a tRNS\n            }\n            break;\n         }\n\n         case STBI__PNG_TYPE('P','L','T','E'):  {\n            if (first) return stbi__err(\"first not IHDR\", \"Corrupt PNG\");\n            if (c.length > 256*3) return stbi__err(\"invalid PLTE\",\"Corrupt PNG\");\n            pal_len = c.length / 3;\n            if (pal_len * 3 != c.length) return stbi__err(\"invalid PLTE\",\"Corrupt PNG\");\n            for (i=0; i < pal_len; ++i) {\n               palette[i*4+0] = stbi__get8(s);\n               palette[i*4+1] = stbi__get8(s);\n               palette[i*4+2] = stbi__get8(s);\n               palette[i*4+3] = 255;\n            }\n            break;\n         }\n\n         case STBI__PNG_TYPE('t','R','N','S'): {\n            if (first) return stbi__err(\"first not IHDR\", \"Corrupt PNG\");\n            if (z->idata) return stbi__err(\"tRNS after IDAT\",\"Corrupt PNG\");\n            if (pal_img_n) {\n               if (scan == STBI__SCAN_header) { s->img_n = 4; return 1; }\n               if (pal_len == 0) return stbi__err(\"tRNS before PLTE\",\"Corrupt PNG\");\n               if (c.length > pal_len) return stbi__err(\"bad tRNS len\",\"Corrupt PNG\");\n               pal_img_n = 4;\n               for (i=0; i < c.length; ++i)\n                  palette[i*4+3] = stbi__get8(s);\n            } else {\n               if (!(s->img_n & 1)) return stbi__err(\"tRNS with alpha\",\"Corrupt PNG\");\n               if (c.length != (stbi__uint32) s->img_n*2) return stbi__err(\"bad tRNS len\",\"Corrupt PNG\");\n               has_trans = 1;\n               if (z->depth == 16) {\n                  for (k = 0; k < s->img_n; ++k) tc16[k] = (stbi__uint16)stbi__get16be(s); // copy the values as-is\n               } else {\n                  for (k = 0; k < s->img_n; ++k) tc[k] = (stbi_uc)(stbi__get16be(s) & 255) * stbi__depth_scale_table[z->depth]; // non 8-bit images will be larger\n               }\n            }\n            break;\n         }\n\n         case STBI__PNG_TYPE('I','D','A','T'): {\n            if (first) return stbi__err(\"first not IHDR\", \"Corrupt PNG\");\n            if (pal_img_n && !pal_len) return stbi__err(\"no PLTE\",\"Corrupt PNG\");\n            if (scan == STBI__SCAN_header) { s->img_n = pal_img_n; return 1; }\n            if ((int)(ioff + c.length) < (int)ioff) return 0;\n            if (ioff + c.length > idata_limit) {\n               stbi__uint32 idata_limit_old = idata_limit;\n               stbi_uc *p;\n               if (idata_limit == 0) idata_limit = c.length > 4096 ? c.length : 4096;\n               while (ioff + c.length > idata_limit)\n                  idata_limit *= 2;\n               STBI_NOTUSED(idata_limit_old);\n               p = (stbi_uc *) STBI_REALLOC_SIZED(z->idata, idata_limit_old, idata_limit); if (p == NULL) return stbi__err(\"outofmem\", \"Out of memory\");\n               z->idata = p;\n            }\n            if (!stbi__getn(s, z->idata+ioff,c.length)) return stbi__err(\"outofdata\",\"Corrupt PNG\");\n            ioff += c.length;\n            break;\n         }\n\n         case STBI__PNG_TYPE('I','E','N','D'): {\n            stbi__uint32 raw_len, bpl;\n            if (first) return stbi__err(\"first not IHDR\", \"Corrupt PNG\");\n            if (scan != STBI__SCAN_load) return 1;\n            if (z->idata == NULL) return stbi__err(\"no IDAT\",\"Corrupt PNG\");\n            // initial guess for decoded data size to avoid unnecessary reallocs\n            bpl = (s->img_x * z->depth + 7) / 8; // bytes per line, per component\n            raw_len = bpl * s->img_y * s->img_n /* pixels */ + s->img_y /* filter mode per row */;\n            z->expanded = (stbi_uc *) stbi_zlib_decode_malloc_guesssize_headerflag((char *) z->idata, ioff, raw_len, (int *) &raw_len, !is_iphone);\n            if (z->expanded == NULL) return 0; // zlib should set error\n            STBI_FREE(z->idata); z->idata = NULL;\n            if ((req_comp == s->img_n+1 && req_comp != 3 && !pal_img_n) || has_trans)\n               s->img_out_n = s->img_n+1;\n            else\n               s->img_out_n = s->img_n;\n            if (!stbi__create_png_image(z, z->expanded, raw_len, s->img_out_n, z->depth, color, interlace)) return 0;\n            if (has_trans) {\n               if (z->depth == 16) {\n                  if (!stbi__compute_transparency16(z, tc16, s->img_out_n)) return 0;\n               } else {\n                  if (!stbi__compute_transparency(z, tc, s->img_out_n)) return 0;\n               }\n            }\n            if (is_iphone && stbi__de_iphone_flag && s->img_out_n > 2)\n               stbi__de_iphone(z);\n            if (pal_img_n) {\n               // pal_img_n == 3 or 4\n               s->img_n = pal_img_n; // record the actual colors we had\n               s->img_out_n = pal_img_n;\n               if (req_comp >= 3) s->img_out_n = req_comp;\n               if (!stbi__expand_png_palette(z, palette, pal_len, s->img_out_n))\n                  return 0;\n            } else if (has_trans) {\n               // non-paletted image with tRNS -> source image has (constant) alpha\n               ++s->img_n;\n            }\n            STBI_FREE(z->expanded); z->expanded = NULL;\n            return 1;\n         }\n\n         default:\n            // if critical, fail\n            if (first) return stbi__err(\"first not IHDR\", \"Corrupt PNG\");\n            if ((c.type & (1 << 29)) == 0) {\n               #ifndef STBI_NO_FAILURE_STRINGS\n               // not threadsafe\n               static char invalid_chunk[] = \"XXXX PNG chunk not known\";\n               invalid_chunk[0] = STBI__BYTECAST(c.type >> 24);\n               invalid_chunk[1] = STBI__BYTECAST(c.type >> 16);\n               invalid_chunk[2] = STBI__BYTECAST(c.type >>  8);\n               invalid_chunk[3] = STBI__BYTECAST(c.type >>  0);\n               #endif\n               return stbi__err(invalid_chunk, \"PNG not supported: unknown PNG chunk type\");\n            }\n            stbi__skip(s, c.length);\n            break;\n      }\n      // end of PNG chunk, read and skip CRC\n      stbi__get32be(s);\n   }\n}\n\nstatic void *stbi__do_png(stbi__png *p, int *x, int *y, int *n, int req_comp, stbi__result_info *ri)\n{\n   void *result=NULL;\n   if (req_comp < 0 || req_comp > 4) return stbi__errpuc(\"bad req_comp\", \"Internal error\");\n   if (stbi__parse_png_file(p, STBI__SCAN_load, req_comp)) {\n      if (p->depth < 8)\n         ri->bits_per_channel = 8;\n      else\n         ri->bits_per_channel = p->depth;\n      result = p->out;\n      p->out = NULL;\n      if (req_comp && req_comp != p->s->img_out_n) {\n         result = stbi__convert_format((unsigned char *) result, p->s->img_out_n, req_comp, p->s->img_x, p->s->img_y);\n         p->s->img_out_n = req_comp;\n         if (result == NULL) return result;\n      }\n      *x = p->s->img_x;\n      *y = p->s->img_y;\n      if (n) *n = p->s->img_n;\n   }\n   STBI_FREE(p->out);      p->out      = NULL;\n   STBI_FREE(p->expanded); p->expanded = NULL;\n   STBI_FREE(p->idata);    p->idata    = NULL;\n\n   return result;\n}\n\nstatic void *stbi__png_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri)\n{\n   stbi__png p;\n   p.s = s;\n   return stbi__do_png(&p, x,y,comp,req_comp, ri);\n}\n\nstatic int stbi__png_test(stbi__context *s)\n{\n   int r;\n   r = stbi__check_png_header(s);\n   stbi__rewind(s);\n   return r;\n}\n\nstatic int stbi__png_info_raw(stbi__png *p, int *x, int *y, int *comp)\n{\n   if (!stbi__parse_png_file(p, STBI__SCAN_header, 0)) {\n      stbi__rewind( p->s );\n      return 0;\n   }\n   if (x) *x = p->s->img_x;\n   if (y) *y = p->s->img_y;\n   if (comp) *comp = p->s->img_n;\n   return 1;\n}\n\nstatic int stbi__png_info(stbi__context *s, int *x, int *y, int *comp)\n{\n   stbi__png p;\n   p.s = s;\n   return stbi__png_info_raw(&p, x, y, comp);\n}\n\nstatic int stbi__info_main(stbi__context *s, int *x, int *y, int *comp)\n{\n   if (stbi__jpeg_info(s, x, y, comp)) return 1;\n\n   if (stbi__png_info(s, x, y, comp))  return 1;\n\n   return stbi__err(\"unknown image type\", \"Image not of any known type, or corrupt\");\n}\n\nSTBIDEF int stbi_info(char const *filename, int *x, int *y, int *comp)\n{\n    FILE *f = stbi__fopen(filename, \"rb\");\n    int result;\n    if (!f) return stbi__err(\"can't fopen\", \"Unable to open file\");\n    result = stbi_info_from_file(f, x, y, comp);\n    fclose(f);\n    return result;\n}\n\nSTBIDEF int stbi_info_from_file(FILE *f, int *x, int *y, int *comp)\n{\n   int r;\n   stbi__context s;\n   long pos = ftell(f);\n   stbi__start_file(&s, f);\n   r = stbi__info_main(&s,x,y,comp);\n   fseek(f,pos,SEEK_SET);\n   return r;\n}\n\nSTBIDEF int stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp)\n{\n   stbi__context s;\n   stbi__start_mem(&s,buffer,len);\n   return stbi__info_main(&s,x,y,comp);\n}\n\nSTBIDEF int stbi_info_from_callbacks(stbi_io_callbacks const *c, void *user, int *x, int *y, int *comp)\n{\n   stbi__context s;\n   stbi__start_callbacks(&s, (stbi_io_callbacks *) c, user);\n   return stbi__info_main(&s,x,y,comp);\n}\n\n\n// STB_IMAGE_WRITE_IMPLEMENTATION\n\n#include <stdarg.h>\n#include <stdlib.h>\n#include <stdio.h>\n#include <string.h>\n#include <math.h>\n\n#if defined(STBIW_MALLOC) && defined(STBIW_FREE) && defined(STBIW_REALLOC)\n// ok\n#elif !defined(STBIW_MALLOC) && !defined(STBIW_FREE) && !defined(STBIW_REALLOC)\n// ok\n#else\n#error \"Must define all or none of STBIW_MALLOC, STBIW_FREE, and STBIW_REALLOC.\"\n#endif\n\n#ifndef STBIW_MALLOC\n#define STBIW_MALLOC(sz)    malloc(sz)\n#define STBIW_REALLOC(p,sz) realloc(p,sz)\n#define STBIW_FREE(p)       free(p)\n#endif\n#ifndef STBIW_MEMMOVE\n#define STBIW_MEMMOVE(a,b,sz) memmove(a,b,sz)\n#endif\n\n\n#ifndef STBIW_ASSERT\n#include <assert.h>\n#define STBIW_ASSERT(x) assert(x)\n#endif\n\n#ifndef _CRT_SECURE_NO_WARNINGS\n    #define _CRT_SECURE_NO_WARNINGS\n    #define STB_UNDEF_CRT_SECURE_NO_WARNINGS\n#endif\n\ntypedef unsigned int stbiw_uint32;\ntypedef int stb_image_write_test[sizeof(stbiw_uint32)==4 ? 1 : -1];\n\nstatic void writefv(FILE *f, const char *fmt, va_list v)\n{\n   while (*fmt) {\n      switch (*fmt++) {\n         case ' ': break;\n         case '1': { unsigned char x = (unsigned char) va_arg(v, int); fputc(x,f); break; }\n         case '2': { int x = va_arg(v,int); unsigned char b[2];\n                     b[0] = (unsigned char) x; b[1] = (unsigned char) (x>>8);\n                     fwrite(b,2,1,f); break; }\n         case '4': { stbiw_uint32 x = va_arg(v,int); unsigned char b[4];\n                     b[0]=(unsigned char)x; b[1]=(unsigned char)(x>>8);\n                     b[2]=(unsigned char)(x>>16); b[3]=(unsigned char)(x>>24);\n                     fwrite(b,4,1,f); break; }\n         default:\n            STBIW_ASSERT(0);\n            return;\n      }\n   }\n}\n\nstatic void write3(FILE *f, unsigned char a, unsigned char b, unsigned char c)\n{\n   unsigned char arr[3];\n   arr[0] = a, arr[1] = b, arr[2] = c;\n   fwrite(arr, 3, 1, f);\n}\n\nstatic void write_pixels(FILE *f, int rgb_dir, int vdir, int x, int y, int comp, void *data, int write_alpha, int scanline_pad, int expand_mono)\n{\n   unsigned char bg[3] = { 255, 0, 255}, px[3];\n   stbiw_uint32 zero = 0;\n   int i,j,k, j_end;\n\n   if (y <= 0)\n      return;\n\n   if (vdir < 0)\n      j_end = -1, j = y-1;\n   else\n      j_end =  y, j = 0;\n\n   for (; j != j_end; j += vdir) {\n      for (i=0; i < x; ++i) {\n         unsigned char *d = (unsigned char *) data + (j*x+i)*comp;\n         if (write_alpha < 0)\n            fwrite(&d[comp-1], 1, 1, f);\n         switch (comp) {\n            case 1: fwrite(d, 1, 1, f);\n                    break;\n            case 2: if (expand_mono)\n                       write3(f, d[0],d[0],d[0]); // monochrome bmp\n                    else\n                       fwrite(d, 1, 1, f);  // monochrome TGA\n                    break;\n            case 4:\n               if (!write_alpha) {\n                  // composite against pink background\n                  for (k=0; k < 3; ++k)\n                     px[k] = bg[k] + ((d[k] - bg[k]) * d[3])/255;\n                  write3(f, px[1-rgb_dir],px[1],px[1+rgb_dir]);\n                  break;\n               }\n               /* FALLTHROUGH */\n            case 3:\n               write3(f, d[1-rgb_dir],d[1],d[1+rgb_dir]);\n               break;\n         }\n         if (write_alpha > 0)\n            fwrite(&d[comp-1], 1, 1, f);\n      }\n      fwrite(&zero,scanline_pad,1,f);\n   }\n}\n\nstatic int outfile(char const *filename, int rgb_dir, int vdir, int x, int y, int comp, int expand_mono, void *data, int alpha, int pad, const char *fmt, ...)\n{\n   FILE *f;\n   if (y < 0 || x < 0) return 0;\n   f = fopen(filename, \"wb\");\n   if (f) {\n      va_list v;\n      va_start(v, fmt);\n      writefv(f, fmt, v);\n      va_end(v);\n      write_pixels(f,rgb_dir,vdir,x,y,comp,data,alpha,pad,expand_mono);\n      fclose(f);\n   }\n   return f != NULL;\n}\n\nint stbi_write_bmp(char const *filename, int x, int y, int comp, const void *data)\n{\n   int pad = (-x*3) & 3;\n   return outfile(filename,-1,-1,x,y,comp,1,(void *) data,0,pad,\n           \"11 4 22 4\" \"4 44 22 444444\",\n           'B', 'M', 14+40+(x*3+pad)*y, 0,0, 14+40,  // file header\n            40, x,y, 1,24, 0,0,0,0,0,0);             // bitmap header\n}\n\nint stbi_write_tga(char const *filename, int x, int y, int comp, const void *data)\n{\n   int has_alpha = (comp == 2 || comp == 4);\n   int colorbytes = has_alpha ? comp-1 : comp;\n   int format = colorbytes < 2 ? 3 : 2; // 3 color channels (RGB/RGBA) = 2, 1 color channel (Y/YA) = 3\n   return outfile(filename, -1,-1, x, y, comp, 0, (void *) data, has_alpha, 0,\n                  \"111 221 2222 11\", 0,0,format, 0,0,0, 0,0,x,y, (colorbytes+has_alpha)*8, has_alpha*8);\n}\n\n// *************************************************************************************************\n// Radiance RGBE HDR writer\n// by Baldur Karlsson\n#define stbiw__max(a, b)  ((a) > (b) ? (a) : (b))\n\nvoid stbiw__linear_to_rgbe(unsigned char *rgbe, float *linear)\n{\n   int exponent;\n   float maxcomp = stbiw__max(linear[0], stbiw__max(linear[1], linear[2]));\n\n   if (maxcomp < 1e-32) {\n      rgbe[0] = rgbe[1] = rgbe[2] = rgbe[3] = 0;\n   } else {\n      float normalize = (float) frexp(maxcomp, &exponent) * 256.0f/maxcomp;\n\n      rgbe[0] = (unsigned char)(linear[0] * normalize);\n      rgbe[1] = (unsigned char)(linear[1] * normalize);\n      rgbe[2] = (unsigned char)(linear[2] * normalize);\n      rgbe[3] = (unsigned char)(exponent + 128);\n   }\n}\n\n\n/////////////////////////////////////////////////////////\n// PNG\n\n// stretchy buffer; stbiw__sbpush() == vector<>::push_back() -- stbiw__sbcount() == vector<>::size()\n#define stbiw__sbraw(a) ((int *) (a) - 2)\n#define stbiw__sbm(a)   stbiw__sbraw(a)[0]\n#define stbiw__sbn(a)   stbiw__sbraw(a)[1]\n\n#define stbiw__sbneedgrow(a,n)  ((a)==0 || stbiw__sbn(a)+n >= stbiw__sbm(a))\n#define stbiw__sbmaybegrow(a,n) (stbiw__sbneedgrow(a,(n)) ? stbiw__sbgrow(a,n) : 0)\n#define stbiw__sbgrow(a,n)  stbiw__sbgrowf((void **) &(a), (n), sizeof(*(a)))\n\n#define stbiw__sbpush(a, v)      (stbiw__sbmaybegrow(a,1), (a)[stbiw__sbn(a)++] = (v))\n#define stbiw__sbcount(a)        ((a) ? stbiw__sbn(a) : 0)\n#define stbiw__sbfree(a)         ((a) ? STBIW_FREE(stbiw__sbraw(a)),0 : 0)\n\nstatic void *stbiw__sbgrowf(void **arr, int increment, int itemsize)\n{\n   int m = *arr ? 2*stbiw__sbm(*arr)+increment : increment+1;\n   void *p = STBIW_REALLOC(*arr ? stbiw__sbraw(*arr) : 0, itemsize * m + sizeof(int)*2);\n   STBIW_ASSERT(p);\n   if (p) {\n      if (!*arr) ((int *) p)[1] = 0;\n      *arr = (void *) ((int *) p + 2);\n      stbiw__sbm(*arr) = m;\n   }\n   return *arr;\n}\n\nstatic unsigned char *stbiw__zlib_flushf(unsigned char *data, unsigned int *bitbuffer, int *bitcount)\n{\n   while (*bitcount >= 8) {\n      stbiw__sbpush(data, (unsigned char) *bitbuffer);\n      *bitbuffer >>= 8;\n      *bitcount -= 8;\n   }\n   return data;\n}\n\nstatic int stbiw__zlib_bitrev(int code, int codebits)\n{\n   int res=0;\n   while (codebits--) {\n      res = (res << 1) | (code & 1);\n      code >>= 1;\n   }\n   return res;\n}\n\nstatic unsigned int stbiw__zlib_countm(unsigned char *a, unsigned char *b, int limit)\n{\n   int i;\n   for (i=0; i < limit && i < 258; ++i)\n      if (a[i] != b[i]) break;\n   return i;\n}\n\nstatic unsigned int stbiw__zhash(unsigned char *data)\n{\n   stbiw_uint32 hash = data[0] + (data[1] << 8) + (data[2] << 16);\n   hash ^= hash << 3;\n   hash += hash >> 5;\n   hash ^= hash << 4;\n   hash += hash >> 17;\n   hash ^= hash << 25;\n   hash += hash >> 6;\n   return hash;\n}\n\n#define stbiw__zlib_flush() (out = stbiw__zlib_flushf(out, &bitbuf, &bitcount))\n#define stbiw__zlib_add(code,codebits) \\\n      (bitbuf |= (code) << bitcount, bitcount += (codebits), stbiw__zlib_flush())\n#define stbiw__zlib_huffa(b,c)  stbiw__zlib_add(stbiw__zlib_bitrev(b,c),c)\n// default huffman tables\n#define stbiw__zlib_huff1(n)  stbiw__zlib_huffa(0x30 + (n), 8)\n#define stbiw__zlib_huff2(n)  stbiw__zlib_huffa(0x190 + (n)-144, 9)\n#define stbiw__zlib_huff3(n)  stbiw__zlib_huffa(0 + (n)-256,7)\n#define stbiw__zlib_huff4(n)  stbiw__zlib_huffa(0xc0 + (n)-280,8)\n#define stbiw__zlib_huff(n)  ((n) <= 143 ? stbiw__zlib_huff1(n) : (n) <= 255 ? stbiw__zlib_huff2(n) : (n) <= 279 ? stbiw__zlib_huff3(n) : stbiw__zlib_huff4(n))\n#define stbiw__zlib_huffb(n) ((n) <= 143 ? stbiw__zlib_huff1(n) : stbiw__zlib_huff2(n))\n\n#define stbiw__ZHASH   16384\n\nunsigned char * stbi_zlib_compress(unsigned char *data, int data_len, int *out_len, int quality)\n{\n   static unsigned short lengthc[] = { 3,4,5,6,7,8,9,10,11,13,15,17,19,23,27,31,35,43,51,59,67,83,99,115,131,163,195,227,258, 259 };\n   static unsigned char  lengtheb[]= { 0,0,0,0,0,0,0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4,  4,  5,  5,  5,  5,  0 };\n   static unsigned short distc[]   = { 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193,257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577, 32768 };\n   static unsigned char  disteb[]  = { 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13 };\n   unsigned int bitbuf=0;\n   int i,j, bitcount=0;\n   unsigned char *out = NULL;\n   unsigned char **hash_table[stbiw__ZHASH]; // 64KB on the stack!\n   if (quality < 5) quality = 5;\n\n   stbiw__sbpush(out, 0x78);   // DEFLATE 32K window\n   stbiw__sbpush(out, 0x5e);   // FLEVEL = 1\n   stbiw__zlib_add(1,1);  // BFINAL = 1\n   stbiw__zlib_add(1,2);  // BTYPE = 1 -- fixed huffman\n\n   for (i=0; i < stbiw__ZHASH; ++i)\n      hash_table[i] = NULL;\n\n   i=0;\n   while (i < data_len-3) {\n      // hash next 3 bytes of data to be compressed\n      int h = stbiw__zhash(data+i)&(stbiw__ZHASH-1), best=3;\n      unsigned char *bestloc = 0;\n      unsigned char **hlist = hash_table[h];\n      int n = stbiw__sbcount(hlist);\n      for (j=0; j < n; ++j) {\n         if (hlist[j]-data > i-32768) { // if entry lies within window\n            int d = stbiw__zlib_countm(hlist[j], data+i, data_len-i);\n            if (d >= best) best=d,bestloc=hlist[j];\n         }\n      }\n      // when hash table entry is too long, delete half the entries\n      if (hash_table[h] && stbiw__sbn(hash_table[h]) == 2*quality) {\n         STBIW_MEMMOVE(hash_table[h], hash_table[h]+quality, sizeof(hash_table[h][0])*quality);\n         stbiw__sbn(hash_table[h]) = quality;\n      }\n      stbiw__sbpush(hash_table[h],data+i);\n\n      if (bestloc) {\n         // \"lazy matching\" - check match at *next* byte, and if it's better, do cur byte as literal\n         h = stbiw__zhash(data+i+1)&(stbiw__ZHASH-1);\n         hlist = hash_table[h];\n         n = stbiw__sbcount(hlist);\n         for (j=0; j < n; ++j) {\n            if (hlist[j]-data > i-32767) {\n               int e = stbiw__zlib_countm(hlist[j], data+i+1, data_len-i-1);\n               if (e > best) { // if next match is better, bail on current match\n                  bestloc = NULL;\n                  break;\n               }\n            }\n         }\n      }\n\n      if (bestloc) {\n         int d = (int) (data+i - bestloc); // distance back\n         STBIW_ASSERT(d <= 32767 && best <= 258);\n         for (j=0; best > lengthc[j+1]-1; ++j);\n         stbiw__zlib_huff(j+257);\n         if (lengtheb[j]) stbiw__zlib_add(best - lengthc[j], lengtheb[j]);\n         for (j=0; d > distc[j+1]-1; ++j);\n         stbiw__zlib_add(stbiw__zlib_bitrev(j,5),5);\n         if (disteb[j]) stbiw__zlib_add(d - distc[j], disteb[j]);\n         i += best;\n      } else {\n         stbiw__zlib_huffb(data[i]);\n         ++i;\n      }\n   }\n   // write out final bytes\n   for (;i < data_len; ++i)\n      stbiw__zlib_huffb(data[i]);\n   stbiw__zlib_huff(256); // end of block\n   // pad with 0 bits to byte boundary\n   while (bitcount)\n      stbiw__zlib_add(0,1);\n\n   for (i=0; i < stbiw__ZHASH; ++i)\n      (void) stbiw__sbfree(hash_table[i]);\n\n   {\n      // compute adler32 on input\n      unsigned int i=0, s1=1, s2=0, blocklen = data_len % 5552;\n      int j=0;\n      while (j < data_len) {\n         for (i=0; i < blocklen; ++i) s1 += data[j+i], s2 += s1;\n         s1 %= 65521, s2 %= 65521;\n         j += blocklen;\n         blocklen = 5552;\n      }\n      stbiw__sbpush(out, (unsigned char) (s2 >> 8));\n      stbiw__sbpush(out, (unsigned char) s2);\n      stbiw__sbpush(out, (unsigned char) (s1 >> 8));\n      stbiw__sbpush(out, (unsigned char) s1);\n   }\n   *out_len = stbiw__sbn(out);\n   // make returned pointer freeable\n   STBIW_MEMMOVE(stbiw__sbraw(out), out, *out_len);\n   return (unsigned char *) stbiw__sbraw(out);\n}\n\nunsigned int stbiw__crc32(unsigned char *buffer, int len)\n{\n   static unsigned int crc_table[256];\n   unsigned int crc = ~0u;\n   int i,j;\n   if (crc_table[1] == 0)\n      for(i=0; i < 256; i++)\n         for (crc_table[i]=i, j=0; j < 8; ++j)\n            crc_table[i] = (crc_table[i] >> 1) ^ (crc_table[i] & 1 ? 0xedb88320 : 0);\n   for (i=0; i < len; ++i)\n      crc = (crc >> 8) ^ crc_table[buffer[i] ^ (crc & 0xff)];\n   return ~crc;\n}\n\n#define stbiw__wpng4(o,a,b,c,d) ((o)[0]=(unsigned char)(a),(o)[1]=(unsigned char)(b),(o)[2]=(unsigned char)(c),(o)[3]=(unsigned char)(d),(o)+=4)\n#define stbiw__wp32(data,v) stbiw__wpng4(data, (v)>>24,(v)>>16,(v)>>8,(v));\n#define stbiw__wptag(data,s) stbiw__wpng4(data, s[0],s[1],s[2],s[3])\n\nstatic void stbiw__wpcrc(unsigned char **data, int len)\n{\n   unsigned int crc = stbiw__crc32(*data - len - 4, len+4);\n   stbiw__wp32(*data, crc);\n}\n\nstatic unsigned char stbiw__paeth(int a, int b, int c)\n{\n   int p = a + b - c, pa = abs(p-a), pb = abs(p-b), pc = abs(p-c);\n   if (pa <= pb && pa <= pc) return (unsigned char) a;\n   if (pb <= pc) return (unsigned char) b;\n   return (unsigned char) c;\n}\n\nunsigned char *stbi_write_png_to_mem(unsigned char *pixels, int stride_bytes, int x, int y, int n, int *out_len)\n{\n   int ctype[5] = { -1, 0, 4, 2, 6 };\n   unsigned char sig[8] = { 137,80,78,71,13,10,26,10 };\n   unsigned char *out,*o, *filt, *zlib;\n   signed char *line_buffer;\n   int i,j,k,p,zlen;\n\n   if (stride_bytes == 0)\n      stride_bytes = x * n;\n\n   filt = (unsigned char *) STBIW_MALLOC((x*n+1) * y); if (!filt) return 0;\n   line_buffer = (signed char *) STBIW_MALLOC(x * n); if (!line_buffer) { STBIW_FREE(filt); return 0; }\n   for (j=0; j < y; ++j) {\n      static int mapping[] = { 0,1,2,3,4 };\n      static int firstmap[] = { 0,1,0,5,6 };\n      int *mymap = j ? mapping : firstmap;\n      int best = 0, bestval = 0x7fffffff;\n      for (p=0; p < 2; ++p) {\n         for (k= p?best:0; k < 5; ++k) {\n            int type = mymap[k],est=0;\n            unsigned char *z = pixels + stride_bytes*j;\n            for (i=0; i < n; ++i)\n               switch (type) {\n                  case 0: line_buffer[i] = z[i]; break;\n                  case 1: line_buffer[i] = z[i]; break;\n                  case 2: line_buffer[i] = z[i] - z[i-stride_bytes]; break;\n                  case 3: line_buffer[i] = z[i] - (z[i-stride_bytes]>>1); break;\n                  case 4: line_buffer[i] = (signed char) (z[i] - stbiw__paeth(0,z[i-stride_bytes],0)); break;\n                  case 5: line_buffer[i] = z[i]; break;\n                  case 6: line_buffer[i] = z[i]; break;\n               }\n            for (i=n; i < x*n; ++i) {\n               switch (type) {\n                  case 0: line_buffer[i] = z[i]; break;\n                  case 1: line_buffer[i] = z[i] - z[i-n]; break;\n                  case 2: line_buffer[i] = z[i] - z[i-stride_bytes]; break;\n                  case 3: line_buffer[i] = z[i] - ((z[i-n] + z[i-stride_bytes])>>1); break;\n                  case 4: line_buffer[i] = z[i] - stbiw__paeth(z[i-n], z[i-stride_bytes], z[i-stride_bytes-n]); break;\n                  case 5: line_buffer[i] = z[i] - (z[i-n]>>1); break;\n                  case 6: line_buffer[i] = z[i] - stbiw__paeth(z[i-n], 0,0); break;\n               }\n            }\n            if (p) break;\n            for (i=0; i < x*n; ++i)\n               est += abs((signed char) line_buffer[i]);\n            if (est < bestval) { bestval = est; best = k; }\n         }\n      }\n      // when we get here, best contains the filter type, and line_buffer contains the data\n      filt[j*(x*n+1)] = (unsigned char) best;\n      STBIW_MEMMOVE(filt+j*(x*n+1)+1, line_buffer, x*n);\n   }\n   STBIW_FREE(line_buffer);\n   zlib = stbi_zlib_compress(filt, y*( x*n+1), &zlen, 8); // increase 8 to get smaller but use more memory\n   STBIW_FREE(filt);\n   if (!zlib) return 0;\n\n   // each tag requires 12 bytes of overhead\n   out = (unsigned char *) STBIW_MALLOC(8 + 12+13 + 12+zlen + 12);\n   if (!out) return 0;\n   *out_len = 8 + 12+13 + 12+zlen + 12;\n\n   o=out;\n   STBIW_MEMMOVE(o,sig,8); o+= 8;\n   stbiw__wp32(o, 13); // header length\n   stbiw__wptag(o, \"IHDR\");\n   stbiw__wp32(o, x);\n   stbiw__wp32(o, y);\n   *o++ = 8;\n   *o++ = (unsigned char) ctype[n];\n   *o++ = 0;\n   *o++ = 0;\n   *o++ = 0;\n   stbiw__wpcrc(&o,13);\n\n   stbiw__wp32(o, zlen);\n   stbiw__wptag(o, \"IDAT\");\n   STBIW_MEMMOVE(o, zlib, zlen);\n   o += zlen;\n   STBIW_FREE(zlib);\n   stbiw__wpcrc(&o, zlen);\n\n   stbiw__wp32(o,0);\n   stbiw__wptag(o, \"IEND\");\n   stbiw__wpcrc(&o,0);\n\n   STBIW_ASSERT(o == out + *out_len);\n\n   return out;\n}\n\nint stbi_write_png(char const *filename, int x, int y, int comp, const void *data, int stride_bytes)\n{\n   FILE *f;\n   int len;\n   unsigned char *png = stbi_write_png_to_mem((unsigned char *) data, stride_bytes, x, y, comp, &len);\n   if (!png) return 0;\n   f = fopen(filename, \"wb\");\n   if (!f) { STBIW_FREE(png); return 0; }\n   fwrite(png, 1, len, f);\n   fclose(f);\n   STBIW_FREE(png);\n   return 1;\n}\n\n#ifdef STB_UNDEF_CRT_SECURE_NO_WARNINGS\n    #undef _CRT_SECURE_NO_WARNINGS\n    #undef STB_UNDEF_CRT_SECURE_NO_WARNINGS\n#endif\n\n#endif\n"
  }
]