├── .gitignore ├── LICENSE ├── README.md ├── data ├── ag-news │ ├── classes.csv │ ├── test.csv │ └── train.csv └── leaf │ └── leaf.csv ├── images ├── Lena_128.png ├── Lena_32.png ├── Lena_512.png ├── Lena_64.png ├── pixels.png └── tsodinPog.png ├── nob.c ├── raylib ├── LICENSE ├── README ├── libraylib.a ├── raylib.h ├── raymath.h └── rlgl.h ├── src ├── 2d.c ├── 3d.c ├── arena.h ├── common.c ├── knn.c └── nob.h ├── stb ├── stb_ds.h ├── stb_image.h └── stb_image_write.h └── zlib ├── LICENSE ├── libz.a ├── zconf.h └── zlib.h /.gitignore: -------------------------------------------------------------------------------- 1 | nob 2 | nob.old 3 | build/ 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2024 Alexey Kutepov 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the “Software”), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 7 | of the Software, and to permit persons to whom the Software is furnished to do 8 | so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Data Mining in C 2 | 3 | Notes and Source Code from the Data Mining in C series. 4 | 5 | ## Quick Start 6 | 7 | Build is only supported on Linux. 8 | 9 | ```console 10 | $ cc -o nob nob.c 11 | $ ./nob 12 | ``` 13 | -------------------------------------------------------------------------------- /data/ag-news/classes.csv: -------------------------------------------------------------------------------- 1 | World 2 | Sports 3 | Business 4 | Sci/Tech 5 | -------------------------------------------------------------------------------- /data/leaf/leaf.csv: -------------------------------------------------------------------------------- 1 | 1,1,0.72694,1.4742,0.32396,0.98535,1,0.83592,0.0046566,0.0039465,0.04779,0.12795,0.016108,0.0052323,0.00027477,1.1756 2 | 1,2,0.74173,1.5257,0.36116,0.98152,0.99825,0.79867,0.0052423,0.0050016,0.02416,0.090476,0.0081195,0.002708,7.4846e-05,0.69659 3 | 1,3,0.76722,1.5725,0.38998,0.97755,1,0.80812,0.0074573,0.010121,0.011897,0.057445,0.0032891,0.00092068,3.7886e-05,0.44348 4 | 1,4,0.73797,1.4597,0.35376,0.97566,1,0.81697,0.0068768,0.0086068,0.01595,0.065491,0.0042707,0.0011544,6.6272e-05,0.58785 5 | 1,5,0.82301,1.7707,0.44462,0.97698,1,0.75493,0.007428,0.010042,0.0079379,0.045339,0.0020514,0.00055986,2.3504e-05,0.34214 6 | 1,6,0.72997,1.4892,0.34284,0.98755,1,0.84482,0.0049451,0.0044506,0.010487,0.058528,0.0034138,0.0011248,2.4798e-05,0.34068 7 | 1,7,0.82063,1.7529,0.44458,0.97964,0.99649,0.7677,0.0059279,0.0063954,0.018375,0.080587,0.0064523,0.0022713,4.1495e-05,0.53904 8 | 1,8,0.77982,1.6215,0.39222,0.98512,0.99825,0.80816,0.0050987,0.0047314,0.024875,0.089686,0.0079794,0.0024664,0.00014676,0.66975 9 | 1,9,0.83089,1.8199,0.45693,0.9824,1,0.77106,0.0060055,0.006564,0.0072447,0.040616,0.0016469,0.00038812,3.2863e-05,0.33696 10 | 1,10,0.90631,2.3906,0.58336,0.97683,0.99825,0.66419,0.0084019,0.012848,0.0070096,0.042347,0.0017901,0.00045889,2.8251e-05,0.28082 11 | 1,11,0.7459,1.4927,0.34116,0.98296,1,0.83088,0.0055665,0.0056395,0.0057679,0.036511,0.0013313,0.00030872,3.1839e-05,0.25026 12 | 1,12,0.79606,1.6934,0.43387,0.98181,1,0.76985,0.0077992,0.011071,0.013677,0.057832,0.0033334,0.00081648,0.00013855,0.49751 13 | 2,1,0.93361,2.7582,0.64257,0.98346,1,0.59851,0.0055336,0.0055731,0.029712,0.089889,0.0080153,0.0020648,0.00023883,0.91499 14 | 2,2,0.91186,2.4994,0.60323,0.983,1,0.64916,0.0061494,0.0068823,0.018887,0.072486,0.0052267,0.0014887,8.3271e-05,0.67811 15 | 2,3,0.89063,2.2927,0.56667,0.98732,1,0.66427,0.0028365,0.0014643,0.029272,0.091328,0.0082717,0.0022383,0.00020166,0.87177 16 | 2,4,0.86755,2.009,0.51464,0.98691,1,0.70277,0.0054439,0.0053937,0.030348,0.092063,0.0084044,0.0022541,0.00019854,0.94545 17 | 2,5,0.91852,2.5247,0.61648,0.9787,1,0.63037,0.0050494,0.0046404,0.02309,0.082029,0.0066839,0.0018929,0.00012452,0.71713 18 | 2,6,0.88795,2.2038,0.56218,0.97835,0.99825,0.64158,0.0059242,0.0063874,0.032722,0.092969,0.0085691,0.0021199,0.00027729,1.008 19 | 2,7,0.85121,1.9548,0.4892,0.98622,1,0.70267,0.0039733,0.0028733,0.020258,0.070841,0.0049933,0.0012274,0.00014929,0.74174 20 | 2,8,0.89084,2.2979,0.57815,0.97389,1,0.64598,0.015271,0.042443,0.028461,0.086477,0.0074228,0.0018832,0.00024345,0.91307 21 | 2,9,0.93062,2.8973,0.65828,0.98182,1,0.5795,0.0064894,0.0076645,0.023606,0.072237,0.005191,0.0011217,0.00025558,0.90513 22 | 2,10,0.84113,1.86,0.46549,0.99039,1,0.75976,0.0046759,0.0039793,0.062798,0.13234,0.017213,0.0044528,0.00065523,1.653 23 | 3,1,0.70273,1.2099,0.36317,0.9211,0.98772,0.60555,0.023597,0.10134,0.089301,0.20088,0.038786,0.015895,0.0004049,1.5371 24 | 3,2,0.66307,1.2065,0.32559,0.94952,0.99649,0.75954,0.013388,0.032621,0.021815,0.097143,0.0093485,0.0040284,3.6303e-05,0.5341 25 | 3,3,0.61289,1.0991,0.33117,0.92405,0.98421,0.61661,0.025545,0.11877,0.054687,0.1606,0.025145,0.011672,0.00012121,1.1076 26 | 3,4,0.70668,1.251,0.38111,0.94226,0.99825,0.6925,0.019432,0.068724,0.031587,0.11502,0.013056,0.0053112,8.6352e-05,0.72247 27 | 3,5,0.66889,1.1435,0.3846,0.90355,0.99649,0.60571,0.028329,0.14606,0.057506,0.15931,0.024752,0.010304,0.00018533,1.1365 28 | 3,6,0.50139,1.0066,0.29593,0.91585,0.99825,0.64029,0.021782,0.086347,0.054635,0.1598,0.024899,0.011106,0.00016162,1.0511 29 | 3,7,0.60803,1.0646,0.3446,0.90487,0.99649,0.67517,0.031915,0.18538,0.06245,0.16411,0.026225,0.010602,0.00022964,1.2307 30 | 3,8,0.56599,1.0427,0.35318,0.89086,0.99825,0.62068,0.032971,0.19785,0.026348,0.10589,0.011088,0.0046505,5.2046e-05,0.62671 31 | 3,9,0.68605,1.1632,0.37528,0.91639,0.99825,0.61136,0.030339,0.16753,0.058401,0.16858,0.027633,0.012886,0.00015525,1.0815 32 | 3,10,0.59571,1.084,0.32592,0.92056,0.99825,0.65698,0.024753,0.11151,0.042051,0.14722,0.021213,0.010881,6.468e-05,0.83221 33 | 4,1,0.39432,1.1191,0.17272,0.96806,0.99298,0.73417,0.012499,0.028432,0.073063,0.13718,0.01847,0.0040597,0.00090977,1.8893 34 | 4,2,0.62011,1.3298,0.2836,0.96952,0.99825,0.77871,0.0086562,0.013637,0.04722,0.1186,0.013872,0.0037549,0.00044188,1.2024 35 | 4,3,0.73429,1.5069,0.40827,0.94518,1,0.62558,0.014987,0.040879,0.038113,0.092188,0.0084269,0.0017773,0.00047378,1.335 36 | 4,4,0.64161,1.3004,0.31556,0.93324,0.97895,0.65487,0.017963,0.058729,0.030897,0.08221,0.0067132,0.0014678,0.00029878,1.2359 37 | 4,5,0.64056,1.3166,0.28616,0.97053,0.99649,0.77183,0.0091632,0.015281,0.020413,0.065575,0.0042817,0.00089749,0.00020433,0.86218 38 | 4,6,0.74242,1.5529,0.38804,0.95593,0.99474,0.68188,0.012904,0.030306,0.020679,0.064242,0.0041101,0.00079505,0.00026812,0.87687 39 | 4,7,0.39289,1.1286,0.17039,0.96405,1,0.79407,0.011761,0.025176,0.025409,0.078211,0.0060798,0.0015299,0.00015762,1.0326 40 | 4,8,0.53017,1.2503,0.23254,0.97337,1,0.82703,0.0066263,0.0079913,0.048541,0.10941,0.011828,0.0027809,0.00048839,1.5452 41 | 5,1,0.87844,1.8096,0.63151,0.83923,0.83684,0.37688,0.043563,0.34539,0.045468,0.12554,0.015515,0.0051589,0.00022842,1.1374 42 | 5,2,0.88075,1.736,0.58345,0.83383,0.91754,0.41551,0.040582,0.29973,0.035786,0.10047,0.0099932,0.002759,0.00025802,1.0952 43 | 5,3,0.86545,1.8803,0.62039,0.82443,0.85439,0.33077,0.047,0.40204,0.039518,0.1157,0.01321,0.0042406,0.00020084,1.0136 44 | 5,4,0.93671,2.4151,0.7298,0.81793,0.86491,0.33439,0.080539,1.1805,0.048722,0.12051,0.014314,0.0039983,0.00037216,1.3083 45 | 5,5,0.92676,2.222,0.6558,0.82432,0.89474,0.35618,0.038012,0.26298,0.059981,0.14901,0.021723,0.0075251,0.00028398,1.353 46 | 5,6,0.90944,2.1139,0.69555,0.80873,0.85263,0.31039,0.047267,0.40663,0.039446,0.10517,0.010939,0.0028913,0.00029366,1.1602 47 | 5,7,0.89493,2.0494,0.64925,0.81682,0.83509,0.35318,0.049955,0.45418,0.041808,0.12036,0.01428,0.0048007,0.00017096,1.0962 48 | 5,8,0.93613,2.3898,0.67175,0.84434,0.90526,0.37007,0.039277,0.28077,0.048447,0.11955,0.014091,0.0037887,0.00042459,1.2593 49 | 5,9,0.88172,1.774,0.63974,0.8499,0.87368,0.34354,0.051776,0.4879,0.06103,0.1519,0.022554,0.0080813,0.00025039,1.3805 50 | 5,10,0.86224,1.7434,0.62464,0.85754,0.89298,0.32238,0.050792,0.46952,0.064428,0.16335,0.025989,0.010189,0.00023873,1.3343 51 | 5,11,0.82268,1.6657,0.59909,0.84493,0.85614,0.32056,0.048803,0.43348,0.079282,0.18571,0.033338,0.013523,0.00027502,1.5377 52 | 5,12,0.82135,1.6035,0.6025,0.84981,0.87544,0.31304,0.056079,0.57236,0.069239,0.16319,0.02594,0.0092918,0.00032134,1.4686 53 | 6,1,0.46348,1.0544,0.72231,0.63754,0.47544,0.20039,0.14864,4.0212,0.010917,0.053494,0.0028534,0.00087543,2.9594e-05,0.53199 54 | 6,2,0.50784,1.0544,0.623,0.73625,0.59649,0.27687,0.13296,3.2176,0.014816,0.067044,0.0044748,0.0013737,3.792e-05,0.52114 55 | 6,3,0.47728,1.2115,0.68343,0.76364,0.56667,0.23896,0.10747,2.1022,0.024036,0.080623,0.0064581,0.0018312,0.00010648,0.90702 56 | 6,4,0.52744,1.0419,0.51662,0.77201,0.84211,0.33784,0.11438,2.3811,0.0254,0.082047,0.0066868,0.0017982,0.00012196,0.944 57 | 6,5,0.50964,1.2067,0.70618,0.72175,0.56491,0.21009,0.11264,2.3093,0.021181,0.080306,0.0064077,0.0020323,6.5828e-05,0.73274 58 | 6,6,0.63965,1.2323,0.60663,0.77037,0.62105,0.24135,0.12438,2.8155,0.025438,0.096215,0.0091723,0.0034208,5.1566e-05,0.75194 59 | 6,7,0.71847,1.2291,0.693,0.75574,0.66667,0.26004,0.098823,1.7774,0.021217,0.073201,0.0053299,0.0013612,0.00012452,0.79862 60 | 6,8,0.47815,1.1032,0.52998,0.81654,0.8193,0.33753,0.11547,2.4268,0.006859,0.042071,0.0017668,0.00047564,1.4404e-05,0.30832 61 | 7,1,0.84886,1.7011,0.5348,0.81465,0.97018,0.37276,0.035424,0.22838,0.038846,0.1192,0.014009,0.0057424,0.00010856,1.2269 62 | 7,2,0.83755,1.5634,0.53329,0.76697,0.95088,0.27825,0.039592,0.28528,0.023152,0.087838,0.0076564,0.0028187,5.6309e-05,0.8079 63 | 7,3,0.84315,1.8134,0.54611,0.84406,0.96667,0.49437,0.038509,0.2699,0.054653,0.14411,0.020345,0.0082654,0.00016275,1.495 64 | 7,4,0.82602,1.6637,0.49244,0.8862,0.99298,0.57136,0.022152,0.089306,0.034444,0.1012,0.010138,0.0030708,0.00016513,1.1047 65 | 7,5,0.86486,1.7986,0.55606,0.87745,0.97018,0.52669,0.029555,0.15898,0.027883,0.092866,0.0085504,0.0031577,0.00012091,0.9961 66 | 7,6,0.84563,1.7418,0.49818,0.92748,1,0.62301,0.014708,0.039374,0.025028,0.077667,0.0059959,0.002105,0.00029619,1.1899 67 | 7,7,0.87163,2.0659,0.55339,0.94868,1,0.60231,0.031671,0.18255,0.049706,0.1344,0.017742,0.0066921,0.00016133,1.4016 68 | 7,8,0.86587,1.9999,0.52725,0.97215,0.99474,0.67283,0.0092956,0.015726,0.021192,0.073946,0.0054383,0.0014673,0.00015488,0.78201 69 | 7,9,0.82373,1.6421,0.47458,0.85777,0.98421,0.50374,0.020049,0.073157,0.018698,0.075567,0.005678,0.0023517,8.791e-05,0.7658 70 | 7,10,0.84285,1.8572,0.51931,0.91339,0.97544,0.60526,0.028734,0.15027,0.038283,0.1156,0.013186,0.0048867,0.00012558,1.1185 71 | 8,1,0.98502,6.0596,0.83612,0.97628,0.98772,0.31546,0.02322,0.098133,0.03683,0.10661,0.011238,0.0032908,0.00022206,1.0185 72 | 8,2,0.98487,6.0229,0.83628,0.96522,1,0.29509,0.024667,0.11074,0.0303,0.097095,0.0093394,0.0027833,0.00018341,0.85025 73 | 8,3,0.98853,6.628,0.84928,0.96861,0.97719,0.27717,0.029032,0.1534,0.032722,0.10283,0.010462,0.0033387,0.00013116,0.94985 74 | 8,4,0.98326,5.8308,0.82878,0.96489,1,0.30777,0.014672,0.039178,0.019278,0.073118,0.0053178,0.0014657,9.7882e-05,0.66403 75 | 8,5,0.98592,6.0269,0.83884,0.96027,0.96842,0.28345,0.025004,0.11379,0.021226,0.080869,0.0064972,0.001957,9.1646e-05,0.64839 76 | 8,6,0.98695,6.2904,0.84124,0.97085,1,0.29288,0.023221,0.09814,0.025859,0.089821,0.0080032,0.0024919,9.7883e-05,0.8112 77 | 8,7,0.98717,6.5173,0.84726,0.96846,1,0.28899,0.022056,0.08854,0.042124,0.11842,0.013829,0.0043818,0.00020215,1.098 78 | 8,8,0.98361,5.8719,0.82847,0.95488,0.98421,0.32134,0.040164,0.29359,0.039243,0.11622,0.013327,0.0043872,0.00019465,1.0006 79 | 8,9,0.98643,6.1273,0.84737,0.95274,0.89649,0.29982,0.019421,0.068643,0.028986,0.095642,0.0090645,0.0027526,0.00014425,0.82529 80 | 8,10,0.986,6.1763,0.83942,0.97837,0.99298,0.31607,0.011769,0.025208,0.051048,0.13524,0.017961,0.0060176,0.0003128,1.166 81 | 8,11,0.98824,7.142,0.86069,0.96683,0.99474,0.26951,0.021697,0.08568,0.024361,0.087108,0.0075307,0.0023055,9.7934e-05,0.7396 82 | 9,1,0.47821,1.2059,0.34976,0.90378,0.99649,0.51298,0.030166,0.16561,0.082509,0.17347,0.029213,0.0093049,0.00067871,1.5956 83 | 9,2,0.53941,1.278,0.32286,0.91793,0.97368,0.63545,0.034884,0.22147,0.060967,0.15124,0.022362,0.0076389,0.00043953,1.2541 84 | 9,3,0.54833,1.2733,0.31093,0.93409,0.99825,0.59707,0.017966,0.058744,0.043685,0.12505,0.015397,0.0051037,0.00042441,0.94617 85 | 9,4,0.58938,1.3232,0.40145,0.89009,0.98421,0.43977,0.047907,0.4177,0.11488,0.19211,0.035592,0.0091838,0.0015424,2.1004 86 | 9,5,0.64803,1.392,0.41362,0.89752,0.96316,0.50892,0.026737,0.1301,0.058284,0.15033,0.022099,0.0078269,0.00036426,1.184 87 | 9,6,0.54029,1.2759,0.36992,0.88831,0.97719,0.54283,0.028138,0.1441,0.051554,0.14256,0.019917,0.0074501,0.00020354,1.1427 88 | 9,7,0.4309,1.2825,0.38769,0.85341,0.96667,0.46204,0.052403,0.49979,0.067688,0.16198,0.025566,0.0091042,0.00043085,1.3596 89 | 9,8,0.4659,1.2195,0.27095,0.94433,1,0.66599,0.013322,0.032299,0.024332,0.10405,0.01071,0.0047135,4.6307e-05,0.56447 90 | 9,9,0.4867,1.2165,0.37691,0.90161,0.99474,0.54006,0.032181,0.18848,0.098716,0.18377,0.03267,0.009529,0.00095661,1.9018 91 | 9,10,0.46098,1.2199,0.32967,0.90459,1,0.56329,0.027061,0.13328,0.092156,0.18249,0.03223,0.010076,0.00090962,1.7156 92 | 9,11,0.73086,1.5285,0.46455,0.87291,0.97193,0.4865,0.034289,0.21399,0.02898,0.10191,0.01028,0.0035859,0.00010051,0.76676 93 | 9,12,0.51577,1.2902,0.32454,0.9208,0.99474,0.62017,0.023879,0.10378,0.047501,0.12228,0.014732,0.0042129,0.00056945,1.1232 94 | 9,13,0.59837,1.3539,0.35704,0.93647,0.99825,0.64448,0.019818,0.071484,0.045326,0.13137,0.016966,0.0059817,0.0003532,0.93682 95 | 9,14,0.55112,1.2821,0.39683,0.88375,0.99649,0.49492,0.030576,0.17015,0.062015,0.14621,0.02093,0.0063685,0.00067747,1.2659 96 | 10,1,0.44941,1.1374,0.38458,0.90736,0.97368,0.54595,0.047809,0.41601,0.11385,0.18608,0.033467,0.0082395,0.0012682,2.2422 97 | 10,2,0.46727,1.1876,0.36748,0.91542,0.97719,0.57995,0.051335,0.47962,0.13764,0.18732,0.033899,0.0061636,0.0029358,2.5551 98 | 10,3,0.55362,1.2438,0.39517,0.91092,0.97193,0.51113,0.046323,0.39055,0.083154,0.17465,0.0296,0.0094574,0.00074607,1.5807 99 | 10,4,0.32175,1.1782,0.32151,0.91803,0.97544,0.58719,0.045933,0.384,0.076818,0.14622,0.020931,0.0048174,0.0014091,1.6749 100 | 10,5,0.64414,1.37,0.42697,0.91457,0.96491,0.44764,0.055411,0.55882,0.1203,0.19149,0.035373,0.0084315,0.0018137,2.2137 101 | 10,6,0.55977,1.3442,0.34301,0.9298,0.97544,0.57879,0.053564,0.52218,0.14905,0.25543,0.061249,0.02381,0.00059701,2.413 102 | 10,7,0.50114,1.2499,0.30101,0.94477,0.9807,0.6743,0.0494,0.44415,0.096252,0.23034,0.050382,0.025877,0.00026934,1.4463 103 | 10,8,0.23041,1.1294,0.25496,0.94201,0.97368,0.6084,0.040722,0.3018,0.10957,0.23571,0.052636,0.02418,0.00042353,1.6648 104 | 10,9,0.46094,1.1366,0.22905,0.95565,0.97544,0.71234,0.038286,0.26678,0.11753,0.22041,0.046329,0.016554,0.00064766,1.9866 105 | 10,10,0.54913,1.2769,0.31283,0.93889,0.99123,0.5933,0.041539,0.31404,0.15188,0.2605,0.06355,0.02379,0.000843,2.203 106 | 10,11,0.32168,1.1127,0.27055,0.95426,0.99298,0.70006,0.05426,0.53584,0.10967,0.21507,0.044212,0.016252,0.00061195,1.8452 107 | 10,12,0.38564,1.0471,0.23328,0.95176,0.97368,0.67107,0.050832,0.47028,0.13563,0.24744,0.057693,0.022,0.00086835,1.9757 108 | 10,13,0.36127,1.0573,0.26213,0.93923,0.96667,0.59302,0.04962,0.44811,0.19067,0.28081,0.073089,0.023825,0.0012501,2.6604 109 | 11,1,0.51247,1.1116,0.65626,0.57724,0.59298,0.16867,0.11187,2.2776,0.016001,0.061238,0.0037361,0.00087861,0.00010284,0.6508 110 | 11,2,0.54893,1.1111,0.63983,0.56623,0.6,0.15743,0.13081,3.1143,0.021231,0.079722,0.0063155,0.0019123,7.3919e-05,0.71949 111 | 11,3,0.43425,1.095,0.68828,0.52398,0.54211,0.15396,0.13761,3.4462,0.021147,0.073202,0.0053299,0.00134,0.00016376,0.75496 112 | 11,4,0.38501,1.0656,0.63042,0.51223,0.59123,0.13705,0.12292,2.7498,0.033373,0.098907,0.0096879,0.0027872,0.00021426,1.0015 113 | 11,5,0.26758,1.1316,0.60128,0.54301,0.77368,0.20311,0.15354,4.2904,0.017945,0.07145,0.0050792,0.0014652,7.2478e-05,0.63273 114 | 11,6,0.24465,1.047,0.60511,0.56524,0.79474,0.21788,0.12522,2.854,0.037595,0.127,0.015874,0.006587,0.00010798,0.8331 115 | 11,7,0.39092,1.087,0.68174,0.50961,0.6614,0.15361,0.14082,3.6093,0.028638,0.089135,0.0078824,0.0021177,0.00021044,0.90082 116 | 11,8,0.4042,1.0965,0.65899,0.52833,0.68421,0.1771,0.13017,3.0837,0.029057,0.096836,0.0092902,0.0028927,0.0001265,0.82383 117 | 11,9,0.50692,1.127,0.67203,0.53024,0.75263,0.16792,0.13006,3.0788,0.015279,0.057592,0.0033059,0.00072847,0.00010983,0.67289 118 | 11,10,0.47565,1.0656,0.69172,0.5233,0.49649,0.14133,0.12987,3.0697,0.023977,0.0842,0.0070397,0.0020845,0.00011284,0.77399 119 | 11,11,0.52382,1.1117,0.67175,0.54701,0.62982,0.15157,0.13674,3.4028,0.026434,0.085792,0.0073064,0.0021373,0.00016583,0.90513 120 | 11,12,0.36462,1.0811,0.67755,0.49042,0.68772,0.14118,0.1243,2.8118,0.037866,0.11692,0.013485,0.0046475,0.0001769,0.9229 121 | 11,13,0.52212,1.1191,0.70988,0.50678,0.64912,0.1412,0.13192,3.1674,0.025478,0.085964,0.0073356,0.0021793,0.0001485,0.82809 122 | 11,14,0.38203,1.0405,0.6901,0.48549,0.63684,0.13165,0.11852,2.5565,0.027997,0.093312,0.008632,0.0026591,0.00012469,0.84994 123 | 11,15,0.27123,1.096,0.68075,0.49446,0.53684,0.13088,0.12815,2.9887,0.021943,0.072882,0.0052838,0.0013391,0.00022058,0.80402 124 | 11,16,0.44882,1.0118,0.6301,0.57134,0.81053,0.16187,0.11115,2.2486,0.027309,0.088889,0.0078393,0.0022734,0.00017545,0.86 125 | 12,1,0.88374,2.4765,0.61389,0.8581,0.98246,0.40448,0.054511,0.54081,0.06489,0.14657,0.021032,0.0067378,0.00037204,1.6461 126 | 12,2,0.91579,2.5959,0.63023,0.94977,1,0.4976,0.024404,0.10839,0.067044,0.1528,0.022814,0.0075708,0.00031405,1.6221 127 | 12,3,0.82866,1.9848,0.50917,0.9418,0.99825,0.55942,0.025524,0.11857,0.080103,0.16692,0.027107,0.0086548,0.0004265,1.8038 128 | 12,4,0.87863,2.4734,0.60898,0.87869,0.99123,0.45758,0.056994,0.59119,0.076471,0.16369,0.026095,0.0087482,0.00034348,1.8363 129 | 12,5,0.92876,2.8218,0.66081,0.93335,0.97544,0.43699,0.046152,0.38767,0.050806,0.13408,0.017661,0.0064421,0.00017937,1.4027 130 | 12,6,0.91093,2.6184,0.63236,0.92868,0.99123,0.51584,0.021091,0.080956,0.05094,0.12676,0.015815,0.0047138,0.00031804,1.3323 131 | 12,7,0.92479,2.5638,0.62255,0.96226,0.97895,0.57504,0.0097927,0.017453,0.026156,0.10029,0.009958,0.0040505,4.4661e-05,0.77718 132 | 12,8,0.90037,2.3499,0.60125,0.93713,0.98421,0.46172,0.03166,0.18242,0.096235,0.18081,0.031658,0.0105,0.00050824,2.3052 133 | 12,9,0.8728,2.3151,0.58751,0.90716,0.98772,0.45257,0.058029,0.61287,0.07597,0.17378,0.029313,0.011448,0.00025642,1.6895 134 | 12,10,0.93377,2.8883,0.66665,0.93026,0.96316,0.43032,0.03571,0.23209,0.047675,0.12917,0.016412,0.0057866,0.00016113,1.321 135 | 12,11,0.88185,2.1732,0.57028,0.93013,0.99825,0.45014,0.049062,0.43809,0.072246,0.16601,0.02682,0.010288,0.0002503,1.7405 136 | 12,12,0.87259,2.1845,0.56922,0.93969,0.99474,0.52611,0.044598,0.36199,0.060388,0.14227,0.019839,0.0062705,0.00031494,1.4969 137 | 13,1,0.71763,1.5041,0.33723,0.98803,1,0.82342,0.0040421,0.0029736,0.050064,0.12542,0.015486,0.0046629,0.00027091,1.3598 138 | 13,2,0.73281,1.5437,0.36231,0.98616,1,0.79359,0.0052593,0.0050342,0.046731,0.1203,0.014265,0.0041563,0.00026442,1.2681 139 | 13,3,0.75285,1.5603,0.38474,0.98701,0.99649,0.78037,0.004611,0.0038695,0.046705,0.11723,0.013556,0.0038164,0.00028371,1.3539 140 | 13,4,0.73935,1.5319,0.34987,0.98479,1,0.81067,0.0078079,0.011095,0.027888,0.11472,0.01299,0.0060166,5.0098e-05,0.59895 141 | 13,5,0.63969,1.3612,0.28845,0.98738,1,0.82125,0.0042318,0.0032593,0.080446,0.15813,0.024394,0.0066506,0.00067629,1.8274 142 | 13,6,0.62033,1.3105,0.26312,0.98535,1,0.82478,0.0049896,0.0045311,0.070042,0.14655,0.021025,0.0058018,0.00053019,1.705 143 | 13,7,0.64536,1.3743,0.28162,0.98646,1,0.84494,0.005427,0.0053602,0.026853,0.085949,0.007333,0.0020334,0.00012813,0.94296 144 | 13,8,0.70115,1.4925,0.34048,0.98262,1,0.78473,0.0061511,0.0068862,0.08405,0.17102,0.028418,0.0087725,0.00052855,1.7782 145 | 13,9,0.70275,1.5016,0.34772,0.98776,0.99825,0.80848,0.0037534,0.002564,0.055512,0.12613,0.015659,0.004022,0.00050106,1.4395 146 | 13,10,0.69686,1.4507,0.33575,0.9851,1,0.80461,0.0036582,0.0024356,0.038211,0.10889,0.011717,0.003581,0.00016657,1.1363 147 | 13,11,0.66351,1.4024,0.31179,0.98529,1,0.81091,0.0068704,0.0085907,0.068206,0.13895,0.01894,0.0049098,0.00062422,1.7727 148 | 13,12,0.67362,1.3988,0.30608,0.98301,1,0.79998,0.0087621,0.013973,0.1196,0.17804,0.030726,0.0063197,0.0016787,2.5094 149 | 13,13,0.64314,1.3574,0.28063,0.98631,0.99825,0.81381,0.0052732,0.0050608,0.058531,0.13108,0.016892,0.0044631,0.00048306,1.5136 150 | 14,1,0.91298,2.455,0.61097,0.96979,0.99825,0.5621,0.0095958,0.016758,0.090447,0.1871,0.033823,0.0115,0.000564,1.6879 151 | 14,2,0.9136,2.5213,0.61341,0.97227,1,0.56248,0.0078474,0.011208,0.068427,0.14325,0.020108,0.0055763,0.00057194,1.7187 152 | 14,3,0.91355,2.457,0.61108,0.97034,0.97544,0.5645,0.0077029,0.010799,0.092547,0.18786,0.034088,0.011708,0.00046097,1.8501 153 | 14,4,0.91378,2.4132,0.60362,0.96618,0.99649,0.56153,0.0091377,0.015197,0.10604,0.20316,0.039637,0.013227,0.00072984,1.8526 154 | 14,5,0.85897,1.9723,0.51434,0.97407,0.99825,0.6792,0.0068392,0.0085129,0.046518,0.13848,0.018816,0.0073898,0.00015454,1.014 155 | 14,6,0.91296,2.4862,0.62315,0.96188,1,0.51041,0.010684,0.020775,0.064539,0.13678,0.018365,0.0048636,0.00052757,1.6875 156 | 14,7,0.93802,2.8087,0.65774,0.97091,1,0.58194,0.0067321,0.0082485,0.052218,0.13491,0.017874,0.0057374,0.00030246,1.2161 157 | 14,8,0.94058,3.0366,0.69248,0.93952,0.99474,0.42979,0.031219,0.17738,0.038691,0.1076,0.011445,0.0032156,0.00024816,1.0843 158 | 14,9,0.89672,2.2534,0.57689,0.96488,1,0.57906,0.0081422,0.012066,0.062778,0.1512,0.022351,0.0078553,0.00031192,1.4575 159 | 14,10,0.89507,2.2045,0.55605,0.97701,0.99649,0.64214,0.0053433,0.0051962,0.102,0.19598,0.036988,0.012562,0.00048437,2.0484 160 | 14,11,0.88246,2.1,0.5486,0.96929,0.99825,0.61897,0.0073021,0.0097043,0.10152,0.19717,0.03742,0.012995,0.00049125,2.0094 161 | 14,12,0.90831,2.4038,0.59263,0.97688,0.99825,0.59259,0.0064679,0.0076137,0.063688,0.1472,0.021208,0.0067017,0.00032702,1.5332 162 | 15,1,0.4132,1.0384,0.48465,0.78118,0.87018,0.30478,0.080722,1.1859,0.047303,0.12619,0.015674,0.0058992,0.00019037,1.489 163 | 15,2,0.56324,1.1928,0.474,0.77564,0.93333,0.34791,0.069404,0.87668,0.10401,0.17923,0.031125,0.0094494,0.00074739,2.4883 164 | 15,3,0.48967,1.1232,0.46175,0.80214,0.9,0.31034,0.073462,0.9822,0.10125,0.17926,0.031133,0.010102,0.00068131,2.4808 165 | 15,4,0.60319,1.088,0.51739,0.7638,0.90175,0.23802,0.077022,1.0797,0.10994,0.17976,0.031303,0.0088846,0.00087888,2.6613 166 | 15,5,0.53622,1.2341,0.44979,0.7882,0.94386,0.38426,0.072253,0.95013,0.042992,0.12337,0.014993,0.0056501,0.00013103,1.2646 167 | 15,6,0.37902,1.0738,0.5242,0.71586,0.85439,0.26309,0.065312,0.77635,0.067161,0.14275,0.019969,0.0059582,0.00040634,1.8527 168 | 15,7,0.53693,1.0751,0.55667,0.74395,0.77544,0.27241,0.080278,1.1729,0.091322,0.16265,0.025773,0.007318,0.00080697,2.2704 169 | 15,8,0.51153,1.0767,0.47053,0.77952,0.90702,0.28838,0.076992,1.0789,0.084711,0.17213,0.028776,0.0099656,0.00039644,2.0412 170 | 15,9,0.44271,1.0836,0.49548,0.74434,0.87018,0.26478,0.079481,1.1497,0.10151,0.1726,0.028929,0.0076406,0.00084487,2.389 171 | 15,10,0.30574,1.1176,0.58847,0.66502,0.84737,0.24074,0.10064,1.8435,0.074621,0.14273,0.019966,0.0054026,0.00062391,2.1403 172 | 22,1,0.92522,2.695,0.63571,0.92994,0.99298,0.505,0.040707,0.30158,0.055742,0.15592,0.023734,0.0096617,0.00030237,1.0351 173 | 22,2,0.90557,2.3423,0.58487,0.95943,0.97368,0.55537,0.023542,0.10087,0.045897,0.13433,0.017724,0.0066757,0.00024528,0.9943 174 | 22,3,0.91885,2.638,0.62859,0.96692,0.99123,0.52269,0.020081,0.07339,0.054145,0.1674,0.027257,0.013322,0.00016644,0.8989 175 | 22,4,0.88965,2.3863,0.59051,0.90564,0.98421,0.5136,0.048934,0.43581,0.062886,0.17454,0.029562,0.013364,0.00026357,1.0734 176 | 22,5,0.9456,3.1174,0.70912,0.79489,0.89298,0.33915,0.13457,3.2956,0.052685,0.14875,0.021648,0.008569,0.00025614,1.0503 177 | 22,6,0.91747,2.7054,0.64091,0.87687,0.98947,0.42531,0.089364,1.4534,0.042216,0.12098,0.014424,0.0047692,0.0003061,1.0097 178 | 22,7,0.86049,2.0997,0.532,0.9358,0.98772,0.5343,0.033994,0.21032,0.040254,0.13592,0.01814,0.0080226,0.00010143,0.82059 179 | 22,8,0.89808,2.4056,0.59137,0.93186,0.98596,0.51229,0.029398,0.15729,0.044185,0.15479,0.0234,0.012336,7.6339e-05,0.78887 180 | 22,9,0.86988,2.1277,0.53926,0.96257,1,0.64927,0.027899,0.14166,0.026849,0.096968,0.0093152,0.0032376,0.00010402,0.7271 181 | 22,10,0.86675,2.0417,0.52999,0.94477,0.98947,0.55141,0.028749,0.15043,0.031023,0.12007,0.014211,0.0064538,6.947e-05,0.64966 182 | 22,11,0.96652,3.8162,0.74273,0.82566,0.94737,0.32925,0.1446,3.8052,0.022715,0.10148,0.010193,0.0046292,4.4517e-05,0.52175 183 | 22,12,0.93396,2.9367,0.66689,0.92543,0.99474,0.45676,0.030052,0.16437,0.015159,0.071198,0.0050436,0.0017096,4.8527e-05,0.46762 184 | 23,1,0.61823,1.2435,0.33084,0.94147,0.9614,0.64089,0.024255,0.10708,0.017543,0.09745,0.0094072,0.0051286,1.2841e-05,0.37324 185 | 23,2,0.61717,1.2296,0.34897,0.93189,0.97368,0.60176,0.025375,0.11719,0.012906,0.070197,0.0049035,0.0019323,2.4448e-05,0.37728 186 | 23,3,0.62404,1.1599,0.47899,0.91562,0.85088,0.45154,0.097205,1.7197,0.013983,0.076408,0.0058043,0.0024109,2.5646e-05,0.36029 187 | 23,4,0.48229,1.1574,0.42617,0.91741,0.92456,0.46699,0.063396,0.73147,0.026522,0.105,0.010904,0.0044681,6.7833e-05,0.63468 188 | 23,5,0.5684,1.1876,0.48001,0.91247,0.79474,0.3885,0.089162,1.4469,0.027113,0.10182,0.010261,0.0037888,0.00010673,0.65567 189 | 23,6,0.62733,1.2441,0.50214,0.90049,0.86667,0.47758,0.092591,1.5603,0.02034,0.091645,0.0083289,0.0033696,4.9071e-05,0.49195 190 | 23,7,0.58637,1.1419,0.30339,0.93305,0.92105,0.57323,0.041282,0.31016,0.022886,0.093704,0.0087041,0.0032291,0.00010441,0.54481 191 | 23,8,0.50771,1.1084,0.25071,0.93529,0.98596,0.64927,0.02595,0.12256,0.013493,0.073509,0.0053745,0.0021246,3.1877e-05,0.3475 192 | 23,9,0.6018,1.2254,0.41835,0.94269,0.95263,0.54871,0.058835,0.62999,0.016202,0.074883,0.0055761,0.0018797,7.1264e-05,0.43769 193 | 23,10,0.63548,1.202,0.47365,0.89373,0.86842,0.44347,0.077616,1.0964,0.022403,0.10386,0.010672,0.005084,3.1292e-05,0.48911 194 | 23,11,0.65048,1.2354,0.44987,0.90929,0.89123,0.57594,0.070699,0.9097,0.0093349,0.0592,0.0034924,0.0013047,2.2232e-05,0.26169 195 | 24,1,0.64604,1.4683,0.35457,0.9588,0.99123,0.70641,0.02021,0.074334,0.059026,0.12721,0.015925,0.0037362,0.0012496,1.3596 196 | 24,2,0.55457,1.2955,0.25519,0.97441,0.99825,0.7824,0.012063,0.026483,0.020129,0.076722,0.0058518,0.0016389,0.00012751,0.60629 197 | 24,3,0.4443,1.2035,0.19432,0.98319,1,0.833,0.010317,0.019373,0.029059,0.10265,0.010427,0.003627,0.00011748,0.73255 198 | 24,4,0.6368,1.4266,0.29976,0.9747,0.99825,0.78457,0.013385,0.032608,0.028272,0.084495,0.0070888,0.0017155,0.00031351,0.913 199 | 24,5,0.64168,1.4853,0.3362,0.95562,0.99825,0.72952,0.017439,0.05535,0.02413,0.074641,0.0055403,0.001194,0.00031921,0.82279 200 | 24,6,0.58232,1.343,0.29271,0.95055,0.99825,0.71499,0.026876,0.13146,0.068134,0.13204,0.017136,0.0036798,0.0014697,1.6315 201 | 24,7,0.44567,1.2107,0.20257,0.97104,0.99123,0.80094,0.016177,0.047628,0.020632,0.074115,0.0054631,0.0013865,0.00016174,0.66423 202 | 24,8,0.67925,1.487,0.33465,0.97307,0.98246,0.7713,0.012399,0.02798,0.0075996,0.044132,0.0019439,0.00049059,3.6857e-05,0.28857 203 | 24,9,0.11708,1.0944,0.1745,0.97545,1,0.81957,0.016114,0.047259,0.033007,0.087092,0.0075278,0.001568,0.0006066,1.0292 204 | 24,10,0.53283,1.3078,0.25925,0.96727,1,0.77065,0.017745,0.057312,0.05341,0.12259,0.014806,0.0035923,0.0012358,1.1886 205 | 24,11,0.52828,1.2496,0.2238,0.98317,1,0.82495,0.010372,0.01958,0.047482,0.10274,0.010444,0.0019794,0.0011976,1.341 206 | 24,12,0.35236,1.146,0.20076,0.96876,0.99649,0.71333,0.017729,0.057204,0.033249,0.099068,0.0097191,0.002653,0.00035728,0.87046 207 | 24,13,0.64897,1.4597,0.3304,0.97406,1,0.75323,0.016021,0.046715,0.012025,0.050266,0.0025203,0.00051116,0.00013299,0.49261 208 | 25,1,0.72162,1.4686,0.43475,0.91337,0.98596,0.60738,0.046324,0.39055,0.097628,0.18471,0.032992,0.0097255,0.001043,1.8296 209 | 25,2,0.71573,1.3808,0.37798,0.95035,0.99123,0.68138,0.025077,0.11445,0.10429,0.23183,0.051004,0.023571,0.00044425,1.5025 210 | 25,3,0.81957,1.56,0.49513,0.91036,0.98421,0.60691,0.045977,0.38473,0.097894,0.20665,0.040954,0.015521,0.0007191,1.5452 211 | 25,4,0.83029,1.633,0.49437,0.92597,0.96842,0.62529,0.045331,0.37399,0.13423,0.2648,0.065523,0.029786,0.00074416,1.7493 212 | 25,5,0.75259,1.5852,0.41155,0.91036,0.99298,0.65821,0.044162,0.35495,0.12959,0.25343,0.060352,0.026003,0.00063907,1.813 213 | 25,6,0.7831,1.5389,0.42639,0.94648,0.97544,0.65478,0.038706,0.27266,0.12607,0.24469,0.056492,0.023259,0.00066766,1.8504 214 | 25,7,0.78823,1.6027,0.44995,0.91357,0.95614,0.62781,0.060707,0.67072,0.12958,0.2522,0.059803,0.025228,0.0009264,1.7489 215 | 25,8,0.83094,1.6643,0.49439,0.96016,0.99649,0.65605,0.023841,0.10345,0.12266,0.24766,0.057789,0.025308,0.00078336,1.7001 216 | 25,9,0.7885,1.638,0.43032,0.95417,1,0.71101,0.026783,0.13055,0.12808,0.21441,0.04395,0.013032,0.0010116,2.2764 217 | 26,1,0.73365,1.5028,0.34887,0.9828,1,0.80144,0.0053647,0.005238,0.029255,0.10327,0.010553,0.0043013,9.1642e-05,0.88407 218 | 26,2,0.82775,1.842,0.48769,0.97383,0.99825,0.68405,0.01619,0.047706,0.022041,0.087963,0.0076781,0.0029651,6.8766e-05,0.68174 219 | 26,3,0.55626,1.2516,0.23975,0.9802,1,0.81411,0.0073087,0.009722,0.056955,0.17933,0.031157,0.017403,8.4502e-05,0.98858 220 | 26,4,0.29901,1.0375,0.10761,0.98386,1,0.84936,0.011462,0.023912,0.032883,0.096034,0.0091383,0.0025695,0.00018734,1.0837 221 | 26,5,0.48223,1.1577,0.16908,0.97886,1,0.71192,0.008864,0.0143,0.037174,0.11384,0.012794,0.0044407,0.00014852,1.0089 222 | 26,6,0.5895,1.2708,0.24364,0.98173,0.99649,0.82082,0.0074388,0.010071,0.041688,0.11576,0.013223,0.0041,0.00025724,1.1105 223 | 26,7,0.322,1.0698,0.13626,0.98017,0.99825,0.81953,0.012913,0.030347,0.076267,0.18383,0.03269,0.014159,0.00026309,1.4955 224 | 26,8,0.482,1.1415,0.15235,0.98536,1,0.85572,0.0072199,0.009487,0.021863,0.079283,0.0062465,0.0017479,0.00013143,0.69178 225 | 26,9,0.87402,2.0754,0.55373,0.96281,0.98421,0.56448,0.019656,0.070316,0.037553,0.10946,0.011839,0.0038499,0.00021407,1.0693 226 | 26,10,0.58138,1.2486,0.21485,0.98704,1,0.83511,0.0046582,0.0039492,0.04072,0.11304,0.012618,0.0036743,0.00033322,1.0368 227 | 26,11,0.40974,1.0732,0.14538,0.98155,1,0.8407,0.0042083,0.0032232,0.04953,0.1489,0.021691,0.010419,0.00010258,1.1781 228 | 26,12,0.82556,1.6903,0.47477,0.97153,1,0.70462,0.019154,0.066774,0.025981,0.10427,0.010756,0.0045965,4.4538e-05,0.67306 229 | 27,1,0.60267,1.254,0.20587,0.98883,1,0.80193,0.0084333,0.012944,0.017777,0.061786,0.003803,0.00091963,0.00015231,0.85839 230 | 27,2,0.66906,1.2751,0.30877,0.9877,1,0.85816,0.0048599,0.0042987,0.031488,0.094241,0.0088032,0.0025337,0.00019057,1.0378 231 | 27,3,0.8138,1.737,0.42956,0.98956,1,0.79117,0.0086131,0.013502,0.033871,0.10399,0.010699,0.0035528,0.00015394,1.0284 232 | 27,4,0.72719,1.4779,0.3298,0.99388,1,0.8423,0.0029668,0.0016019,0.02634,0.081903,0.0066634,0.0017846,0.0001939,0.9805 233 | 27,5,0.79923,1.6549,0.40919,0.98964,1,0.815,0.0048968,0.0043641,0.028693,0.091008,0.0082145,0.0025009,0.00013606,0.98834 234 | 27,6,0.81354,1.6924,0.41016,0.99256,1,0.78909,0.0046348,0.0039096,0.031342,0.096493,0.0092251,0.0028704,0.00017007,1.0171 235 | 27,7,0.8561,1.873,0.49654,0.98047,1,0.6913,0.0092289,0.015501,0.047961,0.1039,0.010681,0.0024623,0.00080703,1.6203 236 | 27,8,0.75968,1.5763,0.38954,0.98731,1,0.82323,0.005582,0.0056708,0.034734,0.098208,0.0095526,0.0027074,0.00023404,1.1433 237 | 27,9,0.8031,1.652,0.40268,0.97695,1,0.79833,0.010883,0.021558,0.046764,0.10227,0.010352,0.0021515,0.00074063,1.5053 238 | 27,10,0.79389,1.5303,0.42937,0.96802,0.99649,0.79209,0.016566,0.049949,0.04703,0.11769,0.013661,0.0041812,0.00034846,1.42 239 | 27,11,0.66551,1.3617,0.26804,0.98949,1,0.76603,0.005179,0.0048816,0.017792,0.074392,0.0055037,0.0018166,5.245e-05,0.62317 240 | 28,1,0.946,3.3475,0.7081,0.96212,1,0.45671,0.035434,0.22851,0.054986,0.13106,0.016886,0.0047646,0.00051238,1.3088 241 | 28,2,0.93038,2.8852,0.67525,0.95738,1,0.50964,0.027111,0.13377,0.050703,0.12329,0.014972,0.0041932,0.00032166,1.3848 242 | 28,3,0.91549,2.6796,0.62862,0.96381,1,0.55874,0.019083,0.066276,0.10351,0.19882,0.038028,0.012751,0.00057453,1.9702 243 | 28,4,0.87202,2.1536,0.53817,0.9788,1,0.65619,0.020327,0.075203,0.081306,0.18726,0.033878,0.013211,0.00036142,1.4606 244 | 28,5,0.91328,2.5654,0.61185,0.98179,1,0.60313,0.006726,0.0082335,0.093484,0.19225,0.035644,0.012245,0.00061603,1.6701 245 | 28,6,0.91996,2.6668,0.63083,0.97894,1,0.58002,0.010311,0.019349,0.1125,0.19785,0.037671,0.011205,0.00078875,2.2241 246 | 28,7,0.91677,2.6955,0.62998,0.98275,1,0.59459,0.0049218,0.0044087,0.10467,0.1876,0.033997,0.0096762,0.00085264,2.0585 247 | 28,8,0.91397,2.7289,0.64656,0.96685,0.99298,0.55519,0.022351,0.090925,0.068514,0.1525,0.022728,0.0070313,0.00041076,1.5681 248 | 28,9,0.8762,2.2436,0.56314,0.98425,1,0.65133,0.010195,0.018915,0.14482,0.22485,0.048122,0.013423,0.0012638,2.4685 249 | 28,10,0.92081,2.9198,0.65845,0.96861,0.99825,0.52299,0.011587,0.024437,0.064788,0.16842,0.027581,0.01122,0.00024357,1.2406 250 | 28,11,0.86832,2.141,0.54004,0.98483,1,0.69313,0.009048,0.0149,0.11927,0.2052,0.040405,0.011659,0.0010933,2.1073 251 | 28,12,0.93323,3.222,0.69174,0.95791,1,0.48142,0.022654,0.093403,0.075307,0.17561,0.029915,0.011062,0.00039957,1.4215 252 | 29,1,0.8375,1.9512,0.4905,0.968,0.98246,0.65138,0.016224,0.047908,0.0051195,0.035621,0.0012673,0.0003224,1.1238e-05,0.23514 253 | 29,2,0.83275,2.0014,0.50157,0.9557,0.99649,0.67019,0.028422,0.14702,0.0092534,0.051082,0.0026026,0.00073282,4.0071e-05,0.32567 254 | 29,3,0.84579,2.0052,0.49931,0.96585,0.99825,0.65149,0.019981,0.07266,0.0050343,0.036189,0.0013079,0.00035119,8.7642e-06,0.22547 255 | 29,4,0.74053,1.6378,0.3892,0.9666,1,0.74844,0.025032,0.11404,0.0083497,0.045391,0.0020561,0.00054932,2.9454e-05,0.38449 256 | 29,5,0.82924,1.8903,0.47248,0.96435,0.99825,0.661,0.023836,0.10341,0.0057361,0.037634,0.0014143,0.00036897,1.3273e-05,0.27099 257 | 29,6,0.82256,1.9016,0.47622,0.96505,1,0.69746,0.025026,0.11398,0.0056632,0.033415,0.0011153,0.00022942,3.7442e-05,0.29502 258 | 29,7,0.84501,2.0918,0.51732,0.9451,1,0.6071,0.039781,0.28802,0.0081002,0.044849,0.0020074,0.00050789,2.9215e-05,0.34316 259 | 29,8,0.8187,1.9126,0.47835,0.94904,0.99298,0.58585,0.03806,0.26364,0.0050219,0.034026,0.0011564,0.00029213,1.5463e-05,0.26037 260 | 29,9,0.78965,1.8186,0.45353,0.94929,1,0.63343,0.030371,0.16787,0.008049,0.04345,0.0018843,0.00045429,2.9147e-05,0.36533 261 | 29,10,0.80728,1.857,0.46197,0.95012,0.99298,0.60913,0.028443,0.14724,0.0096387,0.060095,0.0035984,0.0013452,1.6802e-05,0.27688 262 | 29,11,0.80393,1.8829,0.4698,0.93066,0.99298,0.58011,0.043787,0.34894,0.010183,0.061975,0.0038262,0.0014329,1.8914e-05,0.28509 263 | 29,12,0.85451,2.06,0.51569,0.968,0.98421,0.60909,0.025453,0.11791,0.0079224,0.044948,0.0020163,0.00051981,2.6189e-05,0.32598 264 | 30,1,0.50924,1.2166,0.27943,0.93462,0.99123,0.4092,0.016089,0.047113,0.096518,0.17131,0.028511,0.006963,0.0015453,1.851 265 | 30,2,0.52914,1.2066,0.25595,0.93945,0.97895,0.51693,0.019009,0.065765,0.14675,0.19841,0.037876,0.0073471,0.0023476,2.7085 266 | 30,3,0.2458,1.1011,0.20617,0.94995,0.99298,0.53189,0.012818,0.029901,0.14279,0.20537,0.04047,0.0090273,0.0018094,2.579 267 | 30,4,0.55518,1.2318,0.25226,0.94783,0.99123,0.54965,0.015115,0.041582,0.059148,0.13996,0.019211,0.0057336,0.00042099,1.3579 268 | 30,5,0.35141,1.1428,0.24265,0.93638,0.99123,0.47846,0.019721,0.070781,0.1074,0.18794,0.034115,0.0089893,0.0015026,1.9019 269 | 30,6,0.4803,1.2022,0.26797,0.94544,0.99825,0.52139,0.022778,0.09443,0.066259,0.14818,0.021485,0.0061942,0.00073522,1.3669 270 | 30,7,0.19287,1.0551,0.25044,0.93641,0.99474,0.47284,0.019693,0.070581,0.055935,0.1303,0.016694,0.0045832,0.00044001,1.4111 271 | 30,8,0.49634,1.1832,0.22855,0.94687,0.98947,0.58613,0.017327,0.054638,0.047106,0.12748,0.015992,0.0051731,0.00023865,1.1489 272 | 30,9,0.33254,1.1208,0.27473,0.93625,0.99474,0.49836,0.024394,0.1083,0.10809,0.16882,0.027709,0.0059808,0.0012335,2.4866 273 | 30,10,0.39606,1.1647,0.29415,0.94064,0.99298,0.5486,0.025244,0.11598,0.051625,0.12014,0.014228,0.003721,0.00038216,1.4943 274 | 30,11,0.41933,1.1928,0.30567,0.93428,0.98947,0.50688,0.020948,0.079866,0.057408,0.12508,0.015404,0.003903,0.00045984,1.6497 275 | 30,12,0.45767,1.1629,0.24078,0.94275,0.99298,0.63919,0.020279,0.074844,0.028432,0.095746,0.0090841,0.002812,0.0001478,0.78535 276 | 31,1,0.99541,9.4912,0.91397,0.84991,0.97368,0.17429,0.082108,1.227,0.012512,0.060688,0.0036695,0.0010735,5.216e-05,0.41928 277 | 31,2,0.99611,11.006,0.92239,0.86962,0.99123,0.14581,0.064792,0.76404,0.011176,0.057808,0.0033307,0.00098398,3.9298e-05,0.3731 278 | 31,3,0.99767,12.009,0.94529,0.67005,0.94386,0.11114,0.19898,7.2062,0.010215,0.051104,0.0026048,0.00066765,4.5324e-05,0.39905 279 | 31,4,0.99456,9.7351,0.90444,0.93346,0.98421,0.19638,0.020897,0.079479,0.016599,0.075247,0.0056302,0.0019012,4.441e-05,0.48992 280 | 31,5,0.997,12.548,0.92397,0.93876,0.96842,0.16105,0.026941,0.1321,0.010116,0.057812,0.0033311,0.0010891,2.9179e-05,0.3101 281 | 31,6,0.99698,10.913,0.93836,0.70085,0.93684,0.11552,0.15334,4.2796,0.010649,0.055629,0.0030851,0.00089618,3.6128e-05,0.37415 282 | 31,7,0.99551,9.6915,0.91233,0.88256,0.93509,0.17567,0.049695,0.44946,0.013721,0.067649,0.0045556,0.0015063,4.4371e-05,0.41054 283 | 31,8,0.99502,8.991,0.92789,0.68687,0.86842,0.14478,0.18853,6.4689,0.013268,0.068973,0.0047347,0.001673,3.4802e-05,0.37492 284 | 31,9,0.99583,9.1366,0.92503,0.69234,0.90702,0.15101,0.17918,5.8432,0.0054072,0.044172,0.0019474,0.00070285,6.9241e-06,0.1694 285 | 31,10,0.99593,10.12,0.92461,0.80662,0.90702,0.15053,0.12257,2.7342,0.0082902,0.052333,0.0027313,0.00090459,1.5362e-05,0.27303 286 | 31,11,0.99422,9.1985,0.91119,0.8375,0.98421,0.17712,0.055414,0.55887,0.016168,0.079996,0.0063587,0.0024534,5.5348e-05,0.38686 287 | 32,1,0.86224,2.0735,0.52269,0.98686,0.99474,0.70529,0.010097,0.018554,0.041404,0.12163,0.014579,0.0048689,0.00027608,0.9458 288 | 32,2,0.87024,2.1094,0.52863,0.9836,0.99298,0.60784,0.0031737,0.0018332,0.026902,0.091391,0.0082832,0.0024385,0.00016093,0.73904 289 | 32,3,0.86248,2.0398,0.51338,0.98831,1,0.71419,0.0057488,0.0060148,0.052681,0.13941,0.019064,0.006376,0.00045928,1.0667 290 | 32,4,0.90305,2.3455,0.57721,0.98504,1,0.58743,0.0044044,0.0035306,0.022443,0.077826,0.0060204,0.0015394,0.00021043,0.68561 291 | 32,5,0.88566,2.2029,0.54993,0.98635,1,0.59039,0.0037601,0.0025732,0.039469,0.12459,0.015285,0.0056604,0.00020409,0.84271 292 | 32,6,0.88078,2.147,0.54028,0.98989,1,0.71926,0.0031646,0.0018226,0.044894,0.12906,0.016385,0.0056383,0.00032291,0.96576 293 | 32,7,0.88485,2.2398,0.55754,0.97997,0.99825,0.67974,0.0091286,0.015166,0.025658,0.087206,0.0075476,0.0021518,0.00017873,0.75154 294 | 32,8,0.88661,2.2553,0.56472,0.98284,1,0.67537,0.012123,0.026749,0.022251,0.083617,0.0069433,0.0020893,0.00016304,0.60779 295 | 32,9,0.83188,1.8445,0.46807,0.98691,1,0.75011,0.0049353,0.0044331,0.034814,0.10867,0.011672,0.0036787,0.0003005,0.80348 296 | 32,10,0.85278,1.9935,0.50107,0.98449,0.99825,0.72127,0.0068277,0.0084843,0.015559,0.070923,0.005005,0.0015733,6.9367e-05,0.44798 297 | 32,11,0.78676,1.6815,0.41383,0.97686,0.99825,0.7686,0.013524,0.033289,0.016538,0.076334,0.0057932,0.0019786,7.0661e-05,0.43227 298 | 33,1,0.69887,1.4947,0.34694,0.97906,1,0.74423,0.0070445,0.0090318,0.067277,0.15809,0.024383,0.0079843,0.00066857,1.2554 299 | 33,2,0.60555,1.3891,0.29569,0.97439,1,0.73603,0.010821,0.02131,0.1363,0.20829,0.04158,0.010017,0.0020084,2.2733 300 | 33,3,0.79495,1.7056,0.42041,0.98477,1,0.76317,0.0074486,0.010098,0.064127,0.14621,0.020929,0.0061549,0.00065287,1.3662 301 | 33,4,0.75535,1.6662,0.4065,0.97627,1,0.75126,0.0098445,0.017638,0.020477,0.09052,0.0081274,0.0031335,0.00012137,0.4391 302 | 33,5,0.69398,1.4542,0.32457,0.98263,0.99474,0.79449,0.0063863,0.0074227,0.079016,0.16552,0.026666,0.0079652,0.00077178,1.5438 303 | 33,6,0.81992,1.8698,0.47287,0.97829,0.99825,0.65353,0.0074141,0.010004,0.06789,0.142,0.019765,0.004991,0.00096699,1.4857 304 | 33,7,0.68878,1.4399,0.32986,0.98318,1,0.7847,0.0070802,0.0091235,0.083675,0.17243,0.028872,0.008786,0.0010143,1.5313 305 | 33,8,0.72174,1.5301,0.35707,0.97994,0.99649,0.78622,0.013545,0.03339,0.040677,0.11435,0.012908,0.0038198,0.00036093,0.98926 306 | 33,9,0.73067,1.5145,0.34921,0.98686,1,0.81119,0.017693,0.056975,0.081485,0.16841,0.02758,0.0081927,0.00084762,1.552 307 | 33,10,0.65119,1.4026,0.30387,0.9799,1,0.80081,0.0082573,0.012409,0.10567,0.185,0.033093,0.0087245,0.0013911,1.9632 308 | 33,11,0.71999,1.569,0.37089,0.97599,0.99825,0.77145,0.013975,0.035546,0.11997,0.19286,0.035863,0.0086099,0.001837,2.1311 309 | 34,1,0.99571,10.352,0.90609,0.90652,0.97895,0.16732,0.025787,0.12103,0.023523,0.089455,0.0079387,0.0028882,6.9318e-05,0.73774 310 | 34,2,0.99569,9.9653,0.90578,0.91515,0.9807,0.17113,0.023896,0.10392,0.020381,0.082596,0.0067759,0.0024074,5.0484e-05,0.67264 311 | 34,3,0.9967,11.361,0.914,0.91815,0.93684,0.14435,0.019976,0.072629,0.020667,0.085514,0.0072595,0.0028469,4.6487e-05,0.67925 312 | 34,4,0.99841,16.832,0.94116,0.85531,0.9,0.094537,0.035845,0.23384,0.012341,0.066682,0.0044268,0.0017052,1.7724e-05,0.41828 313 | 34,5,0.99799,15.068,0.93667,0.8807,0.95789,0.12132,0.032164,0.18828,0.011686,0.054827,0.002997,0.00086356,4.3787e-05,0.50576 314 | 34,6,0.99512,10.377,0.90564,0.92135,0.99825,0.17941,0.016647,0.050433,0.0204,0.071662,0.0051092,0.0012661,0.00017021,0.71514 315 | 34,7,0.99505,10.736,0.90851,0.92586,0.99649,0.18236,0.028571,0.14856,0.017761,0.06961,0.0048222,0.0013129,0.00013111,0.59218 316 | 34,8,0.99518,10.421,0.90385,0.96551,0.98947,0.20051,0.0077228,0.010855,0.021258,0.080385,0.0064203,0.0019168,9.5565e-05,0.66405 317 | 34,9,0.9953,10.63,0.90598,0.88866,0.95789,0.15967,0.025636,0.11961,0.014782,0.065416,0.0042611,0.0012831,6.4365e-05,0.52178 318 | 34,10,0.99515,10.007,0.90328,0.91594,0.97719,0.16244,0.033129,0.19975,0.017258,0.070646,0.004966,0.0014641,7.1555e-05,0.59946 319 | 34,11,0.99871,19.038,0.94834,0.851,0.90702,0.086183,0.073048,0.97117,0.0078165,0.048089,0.0023072,0.00075332,1.2922e-05,0.34029 320 | 35,1,0.9109,2.5488,0.6106,0.97388,0.99825,0.55818,0.01972,0.070775,0.10132,0.17022,0.028158,0.0074614,0.0010974,2.3847 321 | 35,2,0.90391,2.458,0.5977,0.9861,1,0.61429,0.0067013,0.0081731,0.08902,0.16706,0.027151,0.007219,0.00096593,1.8937 322 | 35,3,0.90755,2.582,0.62394,0.96837,0.99825,0.55674,0.031714,0.18305,0.079387,0.16213,0.025613,0.0074124,0.00069878,1.6951 323 | 35,4,0.91708,2.6498,0.62919,0.98493,1,0.57926,0.0068053,0.0084289,0.082187,0.16877,0.027693,0.0085027,0.00064002,1.7157 324 | 35,5,0.9166,2.6711,0.6333,0.98228,1,0.51619,0.017465,0.055514,0.10368,0.181,0.031721,0.0081985,0.0014211,1.973 325 | 35,6,0.8942,2.4203,0.59323,0.98407,1,0.60794,0.01743,0.055294,0.10102,0.18748,0.033957,0.0098155,0.0011793,1.8275 326 | 35,7,0.93847,3.0198,0.67312,0.98733,1,0.53234,0.0076393,0.010621,0.07629,0.18017,0.03144,0.011932,0.00072251,1.2434 327 | 35,8,0.89518,2.3875,0.581,0.98796,1,0.63833,0.0077926,0.011052,0.07304,0.1591,0.024688,0.0073807,0.00084138,1.408 328 | 35,9,0.91707,2.6504,0.63359,0.96002,0.99298,0.53972,0.012062,0.026481,0.10121,0.18433,0.032861,0.0089657,0.0014149,1.7979 329 | 35,10,0.92535,2.803,0.65133,0.976,1,0.48413,0.015435,0.04336,0.098946,0.18338,0.032533,0.0091801,0.0013297,1.8033 330 | 35,11,0.91861,2.8114,0.64707,0.94843,0.95614,0.51186,0.045037,0.36915,0.041345,0.13297,0.017374,0.0070233,0.00018923,0.83183 331 | 36,1,0.39093,1.1025,0.73351,0.72022,0.69474,0.17954,0.076072,1.0532,0.059213,0.15747,0.024197,0.0095408,0.00024731,1.2042 332 | 36,2,0.47124,1.1349,0.81159,0.65915,0.47368,0.093982,0.096492,1.6945,0.098618,0.21062,0.042478,0.016848,0.00058101,1.5913 333 | 36,3,0.3687,1.0456,0.77124,0.74413,0.77368,0.22278,0.075187,1.0289,0.074488,0.17993,0.031359,0.012414,0.0003469,1.3541 334 | 36,4,0.14986,1.0558,0.77733,0.73454,0.66316,0.1688,0.08041,1.1768,0.10395,0.1806,0.031585,0.0080165,0.0014315,1.9975 335 | 36,5,0.68069,1.1866,0.78745,0.73496,0.6,0.14029,0.072447,0.95524,0.09277,0.18451,0.032923,0.010852,0.00059383,1.8568 336 | 36,6,0.37522,1.1417,0.81725,0.68511,0.58772,0.12523,0.09186,1.5358,0.11488,0.20861,0.041703,0.013344,0.00082033,2.0281 337 | 36,7,0.28064,1.0849,0.75319,0.72152,0.71404,0.13686,0.078996,1.1358,0.14122,0.2183,0.045488,0.012002,0.0015154,2.4059 338 | 36,8,0.35344,1.0329,0.78147,0.70737,0.61579,0.13503,0.089763,1.4664,0.097663,0.20703,0.041101,0.016123,0.00045288,1.6935 339 | 36,9,0.59988,1.1427,0.71532,0.66101,0.47544,0.15747,0.11337,2.3394,0.050389,0.13585,0.018121,0.00619,0.00026454,1.1526 340 | 36,10,0.47195,1.0901,0.85409,0.53598,0.39649,0.078376,0.13227,3.184,0.082007,0.18782,0.034074,0.013487,0.00032855,1.5623 341 | -------------------------------------------------------------------------------- /images/Lena_128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tsoding/data-mining-in-c/2cd2cc4ad484354465fab37f6543db18c9d660ca/images/Lena_128.png -------------------------------------------------------------------------------- /images/Lena_32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tsoding/data-mining-in-c/2cd2cc4ad484354465fab37f6543db18c9d660ca/images/Lena_32.png -------------------------------------------------------------------------------- /images/Lena_512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tsoding/data-mining-in-c/2cd2cc4ad484354465fab37f6543db18c9d660ca/images/Lena_512.png -------------------------------------------------------------------------------- /images/Lena_64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tsoding/data-mining-in-c/2cd2cc4ad484354465fab37f6543db18c9d660ca/images/Lena_64.png -------------------------------------------------------------------------------- /images/pixels.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tsoding/data-mining-in-c/2cd2cc4ad484354465fab37f6543db18c9d660ca/images/pixels.png -------------------------------------------------------------------------------- /images/tsodinPog.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tsoding/data-mining-in-c/2cd2cc4ad484354465fab37f6543db18c9d660ca/images/tsodinPog.png -------------------------------------------------------------------------------- /nob.c: -------------------------------------------------------------------------------- 1 | #define NOB_IMPLEMENTATION 2 | #include "./src/nob.h" 3 | 4 | Nob_Proc build_program(const char *source_path, const char *output_path) 5 | { 6 | Nob_Cmd cmd = {0}; 7 | nob_cmd_append(&cmd, "cc"); 8 | nob_cmd_append(&cmd, "-Wall", "-Wextra", "-ggdb"); 9 | nob_cmd_append(&cmd, "-I./raylib/", "-I./zlib/", "-I./stb/"); 10 | nob_cmd_append(&cmd, "-O3"); 11 | nob_cmd_append(&cmd, "-o", output_path); 12 | nob_cmd_append(&cmd, source_path); 13 | nob_cmd_append(&cmd, "-L./raylib/", "-L./zlib/"); 14 | nob_cmd_append(&cmd, "-lraylib", "-lm", "-lz", "-ldl", "-lpthread"); 15 | return nob_cmd_run_async(cmd); 16 | } 17 | 18 | int main(int argc, char **argv) 19 | { 20 | NOB_GO_REBUILD_URSELF(argc, argv); 21 | if (!nob_mkdir_if_not_exists("./build/")) return 1; 22 | Nob_Procs procs = {0}; 23 | nob_da_append(&procs, build_program("./src/2d.c", "./build/2d")); 24 | nob_da_append(&procs, build_program("./src/3d.c", "./build/3d")); 25 | nob_da_append(&procs, build_program("./src/knn.c", "./build/knn")); 26 | if (!nob_procs_wait(procs)) return 1; 27 | return 0; 28 | } 29 | -------------------------------------------------------------------------------- /raylib/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013-2023 Ramon Santamaria (@raysan5) 2 | 3 | This software is provided "as-is", without any express or implied warranty. In no event 4 | will the authors be held liable for any damages arising from the use of this software. 5 | 6 | Permission is granted to anyone to use this software for any purpose, including commercial 7 | applications, and to alter it and redistribute it freely, subject to the following restrictions: 8 | 9 | 1. The origin of this software must not be misrepresented; you must not claim that you 10 | wrote the original software. If you use this software in a product, an acknowledgment 11 | in the product documentation would be appreciated but is not required. 12 | 13 | 2. Altered source versions must be plainly marked as such, and must not be misrepresented 14 | as being the original software. 15 | 16 | 3. This notice may not be removed or altered from any source distribution. 17 | -------------------------------------------------------------------------------- /raylib/README: -------------------------------------------------------------------------------- 1 | Custom build of original Raylib 5.0 https://github.com/raysan5/raylib/releases/tag/5.0 -------------------------------------------------------------------------------- /raylib/libraylib.a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tsoding/data-mining-in-c/2cd2cc4ad484354465fab37f6543db18c9d660ca/raylib/libraylib.a -------------------------------------------------------------------------------- /raylib/raymath.h: -------------------------------------------------------------------------------- 1 | /********************************************************************************************** 2 | * 3 | * raymath v1.5 - Math functions to work with Vector2, Vector3, Matrix and Quaternions 4 | * 5 | * CONVENTIONS: 6 | * - Matrix structure is defined as row-major (memory layout) but parameters naming AND all 7 | * math operations performed by the library consider the structure as it was column-major 8 | * It is like transposed versions of the matrices are used for all the maths 9 | * It benefits some functions making them cache-friendly and also avoids matrix 10 | * transpositions sometimes required by OpenGL 11 | * Example: In memory order, row0 is [m0 m4 m8 m12] but in semantic math row0 is [m0 m1 m2 m3] 12 | * - Functions are always self-contained, no function use another raymath function inside, 13 | * required code is directly re-implemented inside 14 | * - Functions input parameters are always received by value (2 unavoidable exceptions) 15 | * - Functions use always a "result" variable for return 16 | * - Functions are always defined inline 17 | * - Angles are always in radians (DEG2RAD/RAD2DEG macros provided for convenience) 18 | * - No compound literals used to make sure libray is compatible with C++ 19 | * 20 | * CONFIGURATION: 21 | * #define RAYMATH_IMPLEMENTATION 22 | * Generates the implementation of the library into the included file. 23 | * If not defined, the library is in header only mode and can be included in other headers 24 | * or source files without problems. But only ONE file should hold the implementation. 25 | * 26 | * #define RAYMATH_STATIC_INLINE 27 | * Define static inline functions code, so #include header suffices for use. 28 | * This may use up lots of memory. 29 | * 30 | * 31 | * LICENSE: zlib/libpng 32 | * 33 | * Copyright (c) 2015-2023 Ramon Santamaria (@raysan5) 34 | * 35 | * This software is provided "as-is", without any express or implied warranty. In no event 36 | * will the authors be held liable for any damages arising from the use of this software. 37 | * 38 | * Permission is granted to anyone to use this software for any purpose, including commercial 39 | * applications, and to alter it and redistribute it freely, subject to the following restrictions: 40 | * 41 | * 1. The origin of this software must not be misrepresented; you must not claim that you 42 | * wrote the original software. If you use this software in a product, an acknowledgment 43 | * in the product documentation would be appreciated but is not required. 44 | * 45 | * 2. Altered source versions must be plainly marked as such, and must not be misrepresented 46 | * as being the original software. 47 | * 48 | * 3. This notice may not be removed or altered from any source distribution. 49 | * 50 | **********************************************************************************************/ 51 | 52 | #ifndef RAYMATH_H 53 | #define RAYMATH_H 54 | 55 | #if defined(RAYMATH_IMPLEMENTATION) && defined(RAYMATH_STATIC_INLINE) 56 | #error "Specifying both RAYMATH_IMPLEMENTATION and RAYMATH_STATIC_INLINE is contradictory" 57 | #endif 58 | 59 | // Function specifiers definition 60 | #if defined(RAYMATH_IMPLEMENTATION) 61 | #if defined(_WIN32) && defined(BUILD_LIBTYPE_SHARED) 62 | #define RMAPI __declspec(dllexport) extern inline // We are building raylib as a Win32 shared library (.dll). 63 | #elif defined(_WIN32) && defined(USE_LIBTYPE_SHARED) 64 | #define RMAPI __declspec(dllimport) // We are using raylib as a Win32 shared library (.dll) 65 | #else 66 | #define RMAPI extern inline // Provide external definition 67 | #endif 68 | #elif defined(RAYMATH_STATIC_INLINE) 69 | #define RMAPI static inline // Functions may be inlined, no external out-of-line definition 70 | #else 71 | #if defined(__TINYC__) 72 | #define RMAPI static inline // plain inline not supported by tinycc (See issue #435) 73 | #else 74 | #define RMAPI inline // Functions may be inlined or external definition used 75 | #endif 76 | #endif 77 | 78 | //---------------------------------------------------------------------------------- 79 | // Defines and Macros 80 | //---------------------------------------------------------------------------------- 81 | #ifndef PI 82 | #define PI 3.14159265358979323846f 83 | #endif 84 | 85 | #ifndef EPSILON 86 | #define EPSILON 0.000001f 87 | #endif 88 | 89 | #ifndef DEG2RAD 90 | #define DEG2RAD (PI/180.0f) 91 | #endif 92 | 93 | #ifndef RAD2DEG 94 | #define RAD2DEG (180.0f/PI) 95 | #endif 96 | 97 | // Get float vector for Matrix 98 | #ifndef MatrixToFloat 99 | #define MatrixToFloat(mat) (MatrixToFloatV(mat).v) 100 | #endif 101 | 102 | // Get float vector for Vector3 103 | #ifndef Vector3ToFloat 104 | #define Vector3ToFloat(vec) (Vector3ToFloatV(vec).v) 105 | #endif 106 | 107 | //---------------------------------------------------------------------------------- 108 | // Types and Structures Definition 109 | //---------------------------------------------------------------------------------- 110 | #if !defined(RL_VECTOR2_TYPE) 111 | // Vector2 type 112 | typedef struct Vector2 { 113 | float x; 114 | float y; 115 | } Vector2; 116 | #define RL_VECTOR2_TYPE 117 | #endif 118 | 119 | #if !defined(RL_VECTOR3_TYPE) 120 | // Vector3 type 121 | typedef struct Vector3 { 122 | float x; 123 | float y; 124 | float z; 125 | } Vector3; 126 | #define RL_VECTOR3_TYPE 127 | #endif 128 | 129 | #if !defined(RL_VECTOR4_TYPE) 130 | // Vector4 type 131 | typedef struct Vector4 { 132 | float x; 133 | float y; 134 | float z; 135 | float w; 136 | } Vector4; 137 | #define RL_VECTOR4_TYPE 138 | #endif 139 | 140 | #if !defined(RL_QUATERNION_TYPE) 141 | // Quaternion type 142 | typedef Vector4 Quaternion; 143 | #define RL_QUATERNION_TYPE 144 | #endif 145 | 146 | #if !defined(RL_MATRIX_TYPE) 147 | // Matrix type (OpenGL style 4x4 - right handed, column major) 148 | typedef struct Matrix { 149 | float m0, m4, m8, m12; // Matrix first row (4 components) 150 | float m1, m5, m9, m13; // Matrix second row (4 components) 151 | float m2, m6, m10, m14; // Matrix third row (4 components) 152 | float m3, m7, m11, m15; // Matrix fourth row (4 components) 153 | } Matrix; 154 | #define RL_MATRIX_TYPE 155 | #endif 156 | 157 | // NOTE: Helper types to be used instead of array return types for *ToFloat functions 158 | typedef struct float3 { 159 | float v[3]; 160 | } float3; 161 | 162 | typedef struct float16 { 163 | float v[16]; 164 | } float16; 165 | 166 | #include // Required for: sinf(), cosf(), tan(), atan2f(), sqrtf(), floor(), fminf(), fmaxf(), fabs() 167 | 168 | //---------------------------------------------------------------------------------- 169 | // Module Functions Definition - Utils math 170 | //---------------------------------------------------------------------------------- 171 | 172 | // Clamp float value 173 | RMAPI float Clamp(float value, float min, float max) 174 | { 175 | float result = (value < min)? min : value; 176 | 177 | if (result > max) result = max; 178 | 179 | return result; 180 | } 181 | 182 | // Calculate linear interpolation between two floats 183 | RMAPI float Lerp(float start, float end, float amount) 184 | { 185 | float result = start + amount*(end - start); 186 | 187 | return result; 188 | } 189 | 190 | // Normalize input value within input range 191 | RMAPI float Normalize(float value, float start, float end) 192 | { 193 | float result = (value - start)/(end - start); 194 | 195 | return result; 196 | } 197 | 198 | // Remap input value within input range to output range 199 | RMAPI float Remap(float value, float inputStart, float inputEnd, float outputStart, float outputEnd) 200 | { 201 | float result = (value - inputStart)/(inputEnd - inputStart)*(outputEnd - outputStart) + outputStart; 202 | 203 | return result; 204 | } 205 | 206 | // Wrap input value from min to max 207 | RMAPI float Wrap(float value, float min, float max) 208 | { 209 | float result = value - (max - min)*floorf((value - min)/(max - min)); 210 | 211 | return result; 212 | } 213 | 214 | // Check whether two given floats are almost equal 215 | RMAPI int FloatEquals(float x, float y) 216 | { 217 | #if !defined(EPSILON) 218 | #define EPSILON 0.000001f 219 | #endif 220 | 221 | int result = (fabsf(x - y)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(x), fabsf(y)))); 222 | 223 | return result; 224 | } 225 | 226 | //---------------------------------------------------------------------------------- 227 | // Module Functions Definition - Vector2 math 228 | //---------------------------------------------------------------------------------- 229 | 230 | // Vector with components value 0.0f 231 | RMAPI Vector2 Vector2Zero(void) 232 | { 233 | Vector2 result = { 0.0f, 0.0f }; 234 | 235 | return result; 236 | } 237 | 238 | // Vector with components value 1.0f 239 | RMAPI Vector2 Vector2One(void) 240 | { 241 | Vector2 result = { 1.0f, 1.0f }; 242 | 243 | return result; 244 | } 245 | 246 | // Add two vectors (v1 + v2) 247 | RMAPI Vector2 Vector2Add(Vector2 v1, Vector2 v2) 248 | { 249 | Vector2 result = { v1.x + v2.x, v1.y + v2.y }; 250 | 251 | return result; 252 | } 253 | 254 | // Add vector and float value 255 | RMAPI Vector2 Vector2AddValue(Vector2 v, float add) 256 | { 257 | Vector2 result = { v.x + add, v.y + add }; 258 | 259 | return result; 260 | } 261 | 262 | // Subtract two vectors (v1 - v2) 263 | RMAPI Vector2 Vector2Subtract(Vector2 v1, Vector2 v2) 264 | { 265 | Vector2 result = { v1.x - v2.x, v1.y - v2.y }; 266 | 267 | return result; 268 | } 269 | 270 | // Subtract vector by float value 271 | RMAPI Vector2 Vector2SubtractValue(Vector2 v, float sub) 272 | { 273 | Vector2 result = { v.x - sub, v.y - sub }; 274 | 275 | return result; 276 | } 277 | 278 | // Calculate vector length 279 | RMAPI float Vector2Length(Vector2 v) 280 | { 281 | float result = sqrtf((v.x*v.x) + (v.y*v.y)); 282 | 283 | return result; 284 | } 285 | 286 | // Calculate vector square length 287 | RMAPI float Vector2LengthSqr(Vector2 v) 288 | { 289 | float result = (v.x*v.x) + (v.y*v.y); 290 | 291 | return result; 292 | } 293 | 294 | // Calculate two vectors dot product 295 | RMAPI float Vector2DotProduct(Vector2 v1, Vector2 v2) 296 | { 297 | float result = (v1.x*v2.x + v1.y*v2.y); 298 | 299 | return result; 300 | } 301 | 302 | // Calculate distance between two vectors 303 | RMAPI float Vector2Distance(Vector2 v1, Vector2 v2) 304 | { 305 | float result = sqrtf((v1.x - v2.x)*(v1.x - v2.x) + (v1.y - v2.y)*(v1.y - v2.y)); 306 | 307 | return result; 308 | } 309 | 310 | // Calculate square distance between two vectors 311 | RMAPI float Vector2DistanceSqr(Vector2 v1, Vector2 v2) 312 | { 313 | float result = ((v1.x - v2.x)*(v1.x - v2.x) + (v1.y - v2.y)*(v1.y - v2.y)); 314 | 315 | return result; 316 | } 317 | 318 | // Calculate angle between two vectors 319 | // NOTE: Angle is calculated from origin point (0, 0) 320 | RMAPI float Vector2Angle(Vector2 v1, Vector2 v2) 321 | { 322 | float result = 0.0f; 323 | 324 | float dot = v1.x*v2.x + v1.y*v2.y; 325 | float det = v1.x*v2.y - v1.y*v2.x; 326 | 327 | result = atan2f(det, dot); 328 | 329 | return result; 330 | } 331 | 332 | // Calculate angle defined by a two vectors line 333 | // NOTE: Parameters need to be normalized 334 | // Current implementation should be aligned with glm::angle 335 | RMAPI float Vector2LineAngle(Vector2 start, Vector2 end) 336 | { 337 | float result = 0.0f; 338 | 339 | // TODO(10/9/2023): Currently angles move clockwise, determine if this is wanted behavior 340 | result = -atan2f(end.y - start.y, end.x - start.x); 341 | 342 | return result; 343 | } 344 | 345 | // Scale vector (multiply by value) 346 | RMAPI Vector2 Vector2Scale(Vector2 v, float scale) 347 | { 348 | Vector2 result = { v.x*scale, v.y*scale }; 349 | 350 | return result; 351 | } 352 | 353 | // Multiply vector by vector 354 | RMAPI Vector2 Vector2Multiply(Vector2 v1, Vector2 v2) 355 | { 356 | Vector2 result = { v1.x*v2.x, v1.y*v2.y }; 357 | 358 | return result; 359 | } 360 | 361 | // Negate vector 362 | RMAPI Vector2 Vector2Negate(Vector2 v) 363 | { 364 | Vector2 result = { -v.x, -v.y }; 365 | 366 | return result; 367 | } 368 | 369 | // Divide vector by vector 370 | RMAPI Vector2 Vector2Divide(Vector2 v1, Vector2 v2) 371 | { 372 | Vector2 result = { v1.x/v2.x, v1.y/v2.y }; 373 | 374 | return result; 375 | } 376 | 377 | // Normalize provided vector 378 | RMAPI Vector2 Vector2Normalize(Vector2 v) 379 | { 380 | Vector2 result = { 0 }; 381 | float length = sqrtf((v.x*v.x) + (v.y*v.y)); 382 | 383 | if (length > 0) 384 | { 385 | float ilength = 1.0f/length; 386 | result.x = v.x*ilength; 387 | result.y = v.y*ilength; 388 | } 389 | 390 | return result; 391 | } 392 | 393 | // Transforms a Vector2 by a given Matrix 394 | RMAPI Vector2 Vector2Transform(Vector2 v, Matrix mat) 395 | { 396 | Vector2 result = { 0 }; 397 | 398 | float x = v.x; 399 | float y = v.y; 400 | float z = 0; 401 | 402 | result.x = mat.m0*x + mat.m4*y + mat.m8*z + mat.m12; 403 | result.y = mat.m1*x + mat.m5*y + mat.m9*z + mat.m13; 404 | 405 | return result; 406 | } 407 | 408 | // Calculate linear interpolation between two vectors 409 | RMAPI Vector2 Vector2Lerp(Vector2 v1, Vector2 v2, float amount) 410 | { 411 | Vector2 result = { 0 }; 412 | 413 | result.x = v1.x + amount*(v2.x - v1.x); 414 | result.y = v1.y + amount*(v2.y - v1.y); 415 | 416 | return result; 417 | } 418 | 419 | // Calculate reflected vector to normal 420 | RMAPI Vector2 Vector2Reflect(Vector2 v, Vector2 normal) 421 | { 422 | Vector2 result = { 0 }; 423 | 424 | float dotProduct = (v.x*normal.x + v.y*normal.y); // Dot product 425 | 426 | result.x = v.x - (2.0f*normal.x)*dotProduct; 427 | result.y = v.y - (2.0f*normal.y)*dotProduct; 428 | 429 | return result; 430 | } 431 | 432 | // Rotate vector by angle 433 | RMAPI Vector2 Vector2Rotate(Vector2 v, float angle) 434 | { 435 | Vector2 result = { 0 }; 436 | 437 | float cosres = cosf(angle); 438 | float sinres = sinf(angle); 439 | 440 | result.x = v.x*cosres - v.y*sinres; 441 | result.y = v.x*sinres + v.y*cosres; 442 | 443 | return result; 444 | } 445 | 446 | // Move Vector towards target 447 | RMAPI Vector2 Vector2MoveTowards(Vector2 v, Vector2 target, float maxDistance) 448 | { 449 | Vector2 result = { 0 }; 450 | 451 | float dx = target.x - v.x; 452 | float dy = target.y - v.y; 453 | float value = (dx*dx) + (dy*dy); 454 | 455 | if ((value == 0) || ((maxDistance >= 0) && (value <= maxDistance*maxDistance))) return target; 456 | 457 | float dist = sqrtf(value); 458 | 459 | result.x = v.x + dx/dist*maxDistance; 460 | result.y = v.y + dy/dist*maxDistance; 461 | 462 | return result; 463 | } 464 | 465 | // Invert the given vector 466 | RMAPI Vector2 Vector2Invert(Vector2 v) 467 | { 468 | Vector2 result = { 1.0f/v.x, 1.0f/v.y }; 469 | 470 | return result; 471 | } 472 | 473 | // Clamp the components of the vector between 474 | // min and max values specified by the given vectors 475 | RMAPI Vector2 Vector2Clamp(Vector2 v, Vector2 min, Vector2 max) 476 | { 477 | Vector2 result = { 0 }; 478 | 479 | result.x = fminf(max.x, fmaxf(min.x, v.x)); 480 | result.y = fminf(max.y, fmaxf(min.y, v.y)); 481 | 482 | return result; 483 | } 484 | 485 | // Clamp the magnitude of the vector between two min and max values 486 | RMAPI Vector2 Vector2ClampValue(Vector2 v, float min, float max) 487 | { 488 | Vector2 result = v; 489 | 490 | float length = (v.x*v.x) + (v.y*v.y); 491 | if (length > 0.0f) 492 | { 493 | length = sqrtf(length); 494 | 495 | if (length < min) 496 | { 497 | float scale = min/length; 498 | result.x = v.x*scale; 499 | result.y = v.y*scale; 500 | } 501 | else if (length > max) 502 | { 503 | float scale = max/length; 504 | result.x = v.x*scale; 505 | result.y = v.y*scale; 506 | } 507 | } 508 | 509 | return result; 510 | } 511 | 512 | // Check whether two given vectors are almost equal 513 | RMAPI int Vector2Equals(Vector2 p, Vector2 q) 514 | { 515 | #if !defined(EPSILON) 516 | #define EPSILON 0.000001f 517 | #endif 518 | 519 | int result = ((fabsf(p.x - q.x)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(p.x), fabsf(q.x))))) && 520 | ((fabsf(p.y - q.y)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(p.y), fabsf(q.y))))); 521 | 522 | return result; 523 | } 524 | 525 | //---------------------------------------------------------------------------------- 526 | // Module Functions Definition - Vector3 math 527 | //---------------------------------------------------------------------------------- 528 | 529 | // Vector with components value 0.0f 530 | RMAPI Vector3 Vector3Zero(void) 531 | { 532 | Vector3 result = { 0.0f, 0.0f, 0.0f }; 533 | 534 | return result; 535 | } 536 | 537 | // Vector with components value 1.0f 538 | RMAPI Vector3 Vector3One(void) 539 | { 540 | Vector3 result = { 1.0f, 1.0f, 1.0f }; 541 | 542 | return result; 543 | } 544 | 545 | // Add two vectors 546 | RMAPI Vector3 Vector3Add(Vector3 v1, Vector3 v2) 547 | { 548 | Vector3 result = { v1.x + v2.x, v1.y + v2.y, v1.z + v2.z }; 549 | 550 | return result; 551 | } 552 | 553 | // Add vector and float value 554 | RMAPI Vector3 Vector3AddValue(Vector3 v, float add) 555 | { 556 | Vector3 result = { v.x + add, v.y + add, v.z + add }; 557 | 558 | return result; 559 | } 560 | 561 | // Subtract two vectors 562 | RMAPI Vector3 Vector3Subtract(Vector3 v1, Vector3 v2) 563 | { 564 | Vector3 result = { v1.x - v2.x, v1.y - v2.y, v1.z - v2.z }; 565 | 566 | return result; 567 | } 568 | 569 | // Subtract vector by float value 570 | RMAPI Vector3 Vector3SubtractValue(Vector3 v, float sub) 571 | { 572 | Vector3 result = { v.x - sub, v.y - sub, v.z - sub }; 573 | 574 | return result; 575 | } 576 | 577 | // Multiply vector by scalar 578 | RMAPI Vector3 Vector3Scale(Vector3 v, float scalar) 579 | { 580 | Vector3 result = { v.x*scalar, v.y*scalar, v.z*scalar }; 581 | 582 | return result; 583 | } 584 | 585 | // Multiply vector by vector 586 | RMAPI Vector3 Vector3Multiply(Vector3 v1, Vector3 v2) 587 | { 588 | Vector3 result = { v1.x*v2.x, v1.y*v2.y, v1.z*v2.z }; 589 | 590 | return result; 591 | } 592 | 593 | // Calculate two vectors cross product 594 | RMAPI Vector3 Vector3CrossProduct(Vector3 v1, Vector3 v2) 595 | { 596 | Vector3 result = { v1.y*v2.z - v1.z*v2.y, v1.z*v2.x - v1.x*v2.z, v1.x*v2.y - v1.y*v2.x }; 597 | 598 | return result; 599 | } 600 | 601 | // Calculate one vector perpendicular vector 602 | RMAPI Vector3 Vector3Perpendicular(Vector3 v) 603 | { 604 | Vector3 result = { 0 }; 605 | 606 | float min = (float) fabs(v.x); 607 | Vector3 cardinalAxis = {1.0f, 0.0f, 0.0f}; 608 | 609 | if (fabsf(v.y) < min) 610 | { 611 | min = (float) fabs(v.y); 612 | Vector3 tmp = {0.0f, 1.0f, 0.0f}; 613 | cardinalAxis = tmp; 614 | } 615 | 616 | if (fabsf(v.z) < min) 617 | { 618 | Vector3 tmp = {0.0f, 0.0f, 1.0f}; 619 | cardinalAxis = tmp; 620 | } 621 | 622 | // Cross product between vectors 623 | result.x = v.y*cardinalAxis.z - v.z*cardinalAxis.y; 624 | result.y = v.z*cardinalAxis.x - v.x*cardinalAxis.z; 625 | result.z = v.x*cardinalAxis.y - v.y*cardinalAxis.x; 626 | 627 | return result; 628 | } 629 | 630 | // Calculate vector length 631 | RMAPI float Vector3Length(const Vector3 v) 632 | { 633 | float result = sqrtf(v.x*v.x + v.y*v.y + v.z*v.z); 634 | 635 | return result; 636 | } 637 | 638 | // Calculate vector square length 639 | RMAPI float Vector3LengthSqr(const Vector3 v) 640 | { 641 | float result = v.x*v.x + v.y*v.y + v.z*v.z; 642 | 643 | return result; 644 | } 645 | 646 | // Calculate two vectors dot product 647 | RMAPI float Vector3DotProduct(Vector3 v1, Vector3 v2) 648 | { 649 | float result = (v1.x*v2.x + v1.y*v2.y + v1.z*v2.z); 650 | 651 | return result; 652 | } 653 | 654 | // Calculate distance between two vectors 655 | RMAPI float Vector3Distance(Vector3 v1, Vector3 v2) 656 | { 657 | float result = 0.0f; 658 | 659 | float dx = v2.x - v1.x; 660 | float dy = v2.y - v1.y; 661 | float dz = v2.z - v1.z; 662 | result = sqrtf(dx*dx + dy*dy + dz*dz); 663 | 664 | return result; 665 | } 666 | 667 | // Calculate square distance between two vectors 668 | RMAPI float Vector3DistanceSqr(Vector3 v1, Vector3 v2) 669 | { 670 | float result = 0.0f; 671 | 672 | float dx = v2.x - v1.x; 673 | float dy = v2.y - v1.y; 674 | float dz = v2.z - v1.z; 675 | result = dx*dx + dy*dy + dz*dz; 676 | 677 | return result; 678 | } 679 | 680 | // Calculate angle between two vectors 681 | RMAPI float Vector3Angle(Vector3 v1, Vector3 v2) 682 | { 683 | float result = 0.0f; 684 | 685 | Vector3 cross = { v1.y*v2.z - v1.z*v2.y, v1.z*v2.x - v1.x*v2.z, v1.x*v2.y - v1.y*v2.x }; 686 | float len = sqrtf(cross.x*cross.x + cross.y*cross.y + cross.z*cross.z); 687 | float dot = (v1.x*v2.x + v1.y*v2.y + v1.z*v2.z); 688 | result = atan2f(len, dot); 689 | 690 | return result; 691 | } 692 | 693 | // Negate provided vector (invert direction) 694 | RMAPI Vector3 Vector3Negate(Vector3 v) 695 | { 696 | Vector3 result = { -v.x, -v.y, -v.z }; 697 | 698 | return result; 699 | } 700 | 701 | // Divide vector by vector 702 | RMAPI Vector3 Vector3Divide(Vector3 v1, Vector3 v2) 703 | { 704 | Vector3 result = { v1.x/v2.x, v1.y/v2.y, v1.z/v2.z }; 705 | 706 | return result; 707 | } 708 | 709 | // Normalize provided vector 710 | RMAPI Vector3 Vector3Normalize(Vector3 v) 711 | { 712 | Vector3 result = v; 713 | 714 | float length = sqrtf(v.x*v.x + v.y*v.y + v.z*v.z); 715 | if (length != 0.0f) 716 | { 717 | float ilength = 1.0f/length; 718 | 719 | result.x *= ilength; 720 | result.y *= ilength; 721 | result.z *= ilength; 722 | } 723 | 724 | return result; 725 | } 726 | 727 | //Calculate the projection of the vector v1 on to v2 728 | RMAPI Vector3 Vector3Project(Vector3 v1, Vector3 v2) 729 | { 730 | Vector3 result = { 0 }; 731 | 732 | float v1dv2 = (v1.x*v2.x + v1.y*v2.y + v1.z*v2.z); 733 | float v2dv2 = (v2.x*v2.x + v2.y*v2.y + v2.z*v2.z); 734 | 735 | float mag = v1dv2/v2dv2; 736 | 737 | result.x = v2.x*mag; 738 | result.y = v2.y*mag; 739 | result.z = v2.z*mag; 740 | 741 | return result; 742 | } 743 | 744 | //Calculate the rejection of the vector v1 on to v2 745 | RMAPI Vector3 Vector3Reject(Vector3 v1, Vector3 v2) 746 | { 747 | Vector3 result = { 0 }; 748 | 749 | float v1dv2 = (v1.x*v2.x + v1.y*v2.y + v1.z*v2.z); 750 | float v2dv2 = (v2.x*v2.x + v2.y*v2.y + v2.z*v2.z); 751 | 752 | float mag = v1dv2/v2dv2; 753 | 754 | result.x = v1.x - (v2.x*mag); 755 | result.y = v1.y - (v2.y*mag); 756 | result.z = v1.z - (v2.z*mag); 757 | 758 | return result; 759 | } 760 | 761 | // Orthonormalize provided vectors 762 | // Makes vectors normalized and orthogonal to each other 763 | // Gram-Schmidt function implementation 764 | RMAPI void Vector3OrthoNormalize(Vector3 *v1, Vector3 *v2) 765 | { 766 | float length = 0.0f; 767 | float ilength = 0.0f; 768 | 769 | // Vector3Normalize(*v1); 770 | Vector3 v = *v1; 771 | length = sqrtf(v.x*v.x + v.y*v.y + v.z*v.z); 772 | if (length == 0.0f) length = 1.0f; 773 | ilength = 1.0f/length; 774 | v1->x *= ilength; 775 | v1->y *= ilength; 776 | v1->z *= ilength; 777 | 778 | // Vector3CrossProduct(*v1, *v2) 779 | Vector3 vn1 = { v1->y*v2->z - v1->z*v2->y, v1->z*v2->x - v1->x*v2->z, v1->x*v2->y - v1->y*v2->x }; 780 | 781 | // Vector3Normalize(vn1); 782 | v = vn1; 783 | length = sqrtf(v.x*v.x + v.y*v.y + v.z*v.z); 784 | if (length == 0.0f) length = 1.0f; 785 | ilength = 1.0f/length; 786 | vn1.x *= ilength; 787 | vn1.y *= ilength; 788 | vn1.z *= ilength; 789 | 790 | // Vector3CrossProduct(vn1, *v1) 791 | Vector3 vn2 = { vn1.y*v1->z - vn1.z*v1->y, vn1.z*v1->x - vn1.x*v1->z, vn1.x*v1->y - vn1.y*v1->x }; 792 | 793 | *v2 = vn2; 794 | } 795 | 796 | // Transforms a Vector3 by a given Matrix 797 | RMAPI Vector3 Vector3Transform(Vector3 v, Matrix mat) 798 | { 799 | Vector3 result = { 0 }; 800 | 801 | float x = v.x; 802 | float y = v.y; 803 | float z = v.z; 804 | 805 | result.x = mat.m0*x + mat.m4*y + mat.m8*z + mat.m12; 806 | result.y = mat.m1*x + mat.m5*y + mat.m9*z + mat.m13; 807 | result.z = mat.m2*x + mat.m6*y + mat.m10*z + mat.m14; 808 | 809 | return result; 810 | } 811 | 812 | // Transform a vector by quaternion rotation 813 | RMAPI Vector3 Vector3RotateByQuaternion(Vector3 v, Quaternion q) 814 | { 815 | Vector3 result = { 0 }; 816 | 817 | result.x = v.x*(q.x*q.x + q.w*q.w - q.y*q.y - q.z*q.z) + v.y*(2*q.x*q.y - 2*q.w*q.z) + v.z*(2*q.x*q.z + 2*q.w*q.y); 818 | result.y = v.x*(2*q.w*q.z + 2*q.x*q.y) + v.y*(q.w*q.w - q.x*q.x + q.y*q.y - q.z*q.z) + v.z*(-2*q.w*q.x + 2*q.y*q.z); 819 | result.z = v.x*(-2*q.w*q.y + 2*q.x*q.z) + v.y*(2*q.w*q.x + 2*q.y*q.z)+ v.z*(q.w*q.w - q.x*q.x - q.y*q.y + q.z*q.z); 820 | 821 | return result; 822 | } 823 | 824 | // Rotates a vector around an axis 825 | RMAPI Vector3 Vector3RotateByAxisAngle(Vector3 v, Vector3 axis, float angle) 826 | { 827 | // Using Euler-Rodrigues Formula 828 | // Ref.: https://en.wikipedia.org/w/index.php?title=Euler%E2%80%93Rodrigues_formula 829 | 830 | Vector3 result = v; 831 | 832 | // Vector3Normalize(axis); 833 | float length = sqrtf(axis.x*axis.x + axis.y*axis.y + axis.z*axis.z); 834 | if (length == 0.0f) length = 1.0f; 835 | float ilength = 1.0f / length; 836 | axis.x *= ilength; 837 | axis.y *= ilength; 838 | axis.z *= ilength; 839 | 840 | angle /= 2.0f; 841 | float a = sinf(angle); 842 | float b = axis.x*a; 843 | float c = axis.y*a; 844 | float d = axis.z*a; 845 | a = cosf(angle); 846 | Vector3 w = { b, c, d }; 847 | 848 | // Vector3CrossProduct(w, v) 849 | Vector3 wv = { w.y*v.z - w.z*v.y, w.z*v.x - w.x*v.z, w.x*v.y - w.y*v.x }; 850 | 851 | // Vector3CrossProduct(w, wv) 852 | Vector3 wwv = { w.y*wv.z - w.z*wv.y, w.z*wv.x - w.x*wv.z, w.x*wv.y - w.y*wv.x }; 853 | 854 | // Vector3Scale(wv, 2*a) 855 | a *= 2; 856 | wv.x *= a; 857 | wv.y *= a; 858 | wv.z *= a; 859 | 860 | // Vector3Scale(wwv, 2) 861 | wwv.x *= 2; 862 | wwv.y *= 2; 863 | wwv.z *= 2; 864 | 865 | result.x += wv.x; 866 | result.y += wv.y; 867 | result.z += wv.z; 868 | 869 | result.x += wwv.x; 870 | result.y += wwv.y; 871 | result.z += wwv.z; 872 | 873 | return result; 874 | } 875 | 876 | // Calculate linear interpolation between two vectors 877 | RMAPI Vector3 Vector3Lerp(Vector3 v1, Vector3 v2, float amount) 878 | { 879 | Vector3 result = { 0 }; 880 | 881 | result.x = v1.x + amount*(v2.x - v1.x); 882 | result.y = v1.y + amount*(v2.y - v1.y); 883 | result.z = v1.z + amount*(v2.z - v1.z); 884 | 885 | return result; 886 | } 887 | 888 | // Calculate reflected vector to normal 889 | RMAPI Vector3 Vector3Reflect(Vector3 v, Vector3 normal) 890 | { 891 | Vector3 result = { 0 }; 892 | 893 | // I is the original vector 894 | // N is the normal of the incident plane 895 | // R = I - (2*N*(DotProduct[I, N])) 896 | 897 | float dotProduct = (v.x*normal.x + v.y*normal.y + v.z*normal.z); 898 | 899 | result.x = v.x - (2.0f*normal.x)*dotProduct; 900 | result.y = v.y - (2.0f*normal.y)*dotProduct; 901 | result.z = v.z - (2.0f*normal.z)*dotProduct; 902 | 903 | return result; 904 | } 905 | 906 | // Get min value for each pair of components 907 | RMAPI Vector3 Vector3Min(Vector3 v1, Vector3 v2) 908 | { 909 | Vector3 result = { 0 }; 910 | 911 | result.x = fminf(v1.x, v2.x); 912 | result.y = fminf(v1.y, v2.y); 913 | result.z = fminf(v1.z, v2.z); 914 | 915 | return result; 916 | } 917 | 918 | // Get max value for each pair of components 919 | RMAPI Vector3 Vector3Max(Vector3 v1, Vector3 v2) 920 | { 921 | Vector3 result = { 0 }; 922 | 923 | result.x = fmaxf(v1.x, v2.x); 924 | result.y = fmaxf(v1.y, v2.y); 925 | result.z = fmaxf(v1.z, v2.z); 926 | 927 | return result; 928 | } 929 | 930 | // Compute barycenter coordinates (u, v, w) for point p with respect to triangle (a, b, c) 931 | // NOTE: Assumes P is on the plane of the triangle 932 | RMAPI Vector3 Vector3Barycenter(Vector3 p, Vector3 a, Vector3 b, Vector3 c) 933 | { 934 | Vector3 result = { 0 }; 935 | 936 | Vector3 v0 = { b.x - a.x, b.y - a.y, b.z - a.z }; // Vector3Subtract(b, a) 937 | Vector3 v1 = { c.x - a.x, c.y - a.y, c.z - a.z }; // Vector3Subtract(c, a) 938 | Vector3 v2 = { p.x - a.x, p.y - a.y, p.z - a.z }; // Vector3Subtract(p, a) 939 | float d00 = (v0.x*v0.x + v0.y*v0.y + v0.z*v0.z); // Vector3DotProduct(v0, v0) 940 | float d01 = (v0.x*v1.x + v0.y*v1.y + v0.z*v1.z); // Vector3DotProduct(v0, v1) 941 | float d11 = (v1.x*v1.x + v1.y*v1.y + v1.z*v1.z); // Vector3DotProduct(v1, v1) 942 | float d20 = (v2.x*v0.x + v2.y*v0.y + v2.z*v0.z); // Vector3DotProduct(v2, v0) 943 | float d21 = (v2.x*v1.x + v2.y*v1.y + v2.z*v1.z); // Vector3DotProduct(v2, v1) 944 | 945 | float denom = d00*d11 - d01*d01; 946 | 947 | result.y = (d11*d20 - d01*d21)/denom; 948 | result.z = (d00*d21 - d01*d20)/denom; 949 | result.x = 1.0f - (result.z + result.y); 950 | 951 | return result; 952 | } 953 | 954 | // Projects a Vector3 from screen space into object space 955 | // NOTE: We are avoiding calling other raymath functions despite available 956 | RMAPI Vector3 Vector3Unproject(Vector3 source, Matrix projection, Matrix view) 957 | { 958 | Vector3 result = { 0 }; 959 | 960 | // Calculate unprojected matrix (multiply view matrix by projection matrix) and invert it 961 | Matrix matViewProj = { // MatrixMultiply(view, projection); 962 | view.m0*projection.m0 + view.m1*projection.m4 + view.m2*projection.m8 + view.m3*projection.m12, 963 | view.m0*projection.m1 + view.m1*projection.m5 + view.m2*projection.m9 + view.m3*projection.m13, 964 | view.m0*projection.m2 + view.m1*projection.m6 + view.m2*projection.m10 + view.m3*projection.m14, 965 | view.m0*projection.m3 + view.m1*projection.m7 + view.m2*projection.m11 + view.m3*projection.m15, 966 | view.m4*projection.m0 + view.m5*projection.m4 + view.m6*projection.m8 + view.m7*projection.m12, 967 | view.m4*projection.m1 + view.m5*projection.m5 + view.m6*projection.m9 + view.m7*projection.m13, 968 | view.m4*projection.m2 + view.m5*projection.m6 + view.m6*projection.m10 + view.m7*projection.m14, 969 | view.m4*projection.m3 + view.m5*projection.m7 + view.m6*projection.m11 + view.m7*projection.m15, 970 | view.m8*projection.m0 + view.m9*projection.m4 + view.m10*projection.m8 + view.m11*projection.m12, 971 | view.m8*projection.m1 + view.m9*projection.m5 + view.m10*projection.m9 + view.m11*projection.m13, 972 | view.m8*projection.m2 + view.m9*projection.m6 + view.m10*projection.m10 + view.m11*projection.m14, 973 | view.m8*projection.m3 + view.m9*projection.m7 + view.m10*projection.m11 + view.m11*projection.m15, 974 | view.m12*projection.m0 + view.m13*projection.m4 + view.m14*projection.m8 + view.m15*projection.m12, 975 | view.m12*projection.m1 + view.m13*projection.m5 + view.m14*projection.m9 + view.m15*projection.m13, 976 | view.m12*projection.m2 + view.m13*projection.m6 + view.m14*projection.m10 + view.m15*projection.m14, 977 | view.m12*projection.m3 + view.m13*projection.m7 + view.m14*projection.m11 + view.m15*projection.m15 }; 978 | 979 | // Calculate inverted matrix -> MatrixInvert(matViewProj); 980 | // Cache the matrix values (speed optimization) 981 | float a00 = matViewProj.m0, a01 = matViewProj.m1, a02 = matViewProj.m2, a03 = matViewProj.m3; 982 | float a10 = matViewProj.m4, a11 = matViewProj.m5, a12 = matViewProj.m6, a13 = matViewProj.m7; 983 | float a20 = matViewProj.m8, a21 = matViewProj.m9, a22 = matViewProj.m10, a23 = matViewProj.m11; 984 | float a30 = matViewProj.m12, a31 = matViewProj.m13, a32 = matViewProj.m14, a33 = matViewProj.m15; 985 | 986 | float b00 = a00*a11 - a01*a10; 987 | float b01 = a00*a12 - a02*a10; 988 | float b02 = a00*a13 - a03*a10; 989 | float b03 = a01*a12 - a02*a11; 990 | float b04 = a01*a13 - a03*a11; 991 | float b05 = a02*a13 - a03*a12; 992 | float b06 = a20*a31 - a21*a30; 993 | float b07 = a20*a32 - a22*a30; 994 | float b08 = a20*a33 - a23*a30; 995 | float b09 = a21*a32 - a22*a31; 996 | float b10 = a21*a33 - a23*a31; 997 | float b11 = a22*a33 - a23*a32; 998 | 999 | // Calculate the invert determinant (inlined to avoid double-caching) 1000 | float invDet = 1.0f/(b00*b11 - b01*b10 + b02*b09 + b03*b08 - b04*b07 + b05*b06); 1001 | 1002 | Matrix matViewProjInv = { 1003 | (a11*b11 - a12*b10 + a13*b09)*invDet, 1004 | (-a01*b11 + a02*b10 - a03*b09)*invDet, 1005 | (a31*b05 - a32*b04 + a33*b03)*invDet, 1006 | (-a21*b05 + a22*b04 - a23*b03)*invDet, 1007 | (-a10*b11 + a12*b08 - a13*b07)*invDet, 1008 | (a00*b11 - a02*b08 + a03*b07)*invDet, 1009 | (-a30*b05 + a32*b02 - a33*b01)*invDet, 1010 | (a20*b05 - a22*b02 + a23*b01)*invDet, 1011 | (a10*b10 - a11*b08 + a13*b06)*invDet, 1012 | (-a00*b10 + a01*b08 - a03*b06)*invDet, 1013 | (a30*b04 - a31*b02 + a33*b00)*invDet, 1014 | (-a20*b04 + a21*b02 - a23*b00)*invDet, 1015 | (-a10*b09 + a11*b07 - a12*b06)*invDet, 1016 | (a00*b09 - a01*b07 + a02*b06)*invDet, 1017 | (-a30*b03 + a31*b01 - a32*b00)*invDet, 1018 | (a20*b03 - a21*b01 + a22*b00)*invDet }; 1019 | 1020 | // Create quaternion from source point 1021 | Quaternion quat = { source.x, source.y, source.z, 1.0f }; 1022 | 1023 | // Multiply quat point by unprojecte matrix 1024 | Quaternion qtransformed = { // QuaternionTransform(quat, matViewProjInv) 1025 | matViewProjInv.m0*quat.x + matViewProjInv.m4*quat.y + matViewProjInv.m8*quat.z + matViewProjInv.m12*quat.w, 1026 | matViewProjInv.m1*quat.x + matViewProjInv.m5*quat.y + matViewProjInv.m9*quat.z + matViewProjInv.m13*quat.w, 1027 | matViewProjInv.m2*quat.x + matViewProjInv.m6*quat.y + matViewProjInv.m10*quat.z + matViewProjInv.m14*quat.w, 1028 | matViewProjInv.m3*quat.x + matViewProjInv.m7*quat.y + matViewProjInv.m11*quat.z + matViewProjInv.m15*quat.w }; 1029 | 1030 | // Normalized world points in vectors 1031 | result.x = qtransformed.x/qtransformed.w; 1032 | result.y = qtransformed.y/qtransformed.w; 1033 | result.z = qtransformed.z/qtransformed.w; 1034 | 1035 | return result; 1036 | } 1037 | 1038 | // Get Vector3 as float array 1039 | RMAPI float3 Vector3ToFloatV(Vector3 v) 1040 | { 1041 | float3 buffer = { 0 }; 1042 | 1043 | buffer.v[0] = v.x; 1044 | buffer.v[1] = v.y; 1045 | buffer.v[2] = v.z; 1046 | 1047 | return buffer; 1048 | } 1049 | 1050 | // Invert the given vector 1051 | RMAPI Vector3 Vector3Invert(Vector3 v) 1052 | { 1053 | Vector3 result = { 1.0f/v.x, 1.0f/v.y, 1.0f/v.z }; 1054 | 1055 | return result; 1056 | } 1057 | 1058 | // Clamp the components of the vector between 1059 | // min and max values specified by the given vectors 1060 | RMAPI Vector3 Vector3Clamp(Vector3 v, Vector3 min, Vector3 max) 1061 | { 1062 | Vector3 result = { 0 }; 1063 | 1064 | result.x = fminf(max.x, fmaxf(min.x, v.x)); 1065 | result.y = fminf(max.y, fmaxf(min.y, v.y)); 1066 | result.z = fminf(max.z, fmaxf(min.z, v.z)); 1067 | 1068 | return result; 1069 | } 1070 | 1071 | // Clamp the magnitude of the vector between two values 1072 | RMAPI Vector3 Vector3ClampValue(Vector3 v, float min, float max) 1073 | { 1074 | Vector3 result = v; 1075 | 1076 | float length = (v.x*v.x) + (v.y*v.y) + (v.z*v.z); 1077 | if (length > 0.0f) 1078 | { 1079 | length = sqrtf(length); 1080 | 1081 | if (length < min) 1082 | { 1083 | float scale = min/length; 1084 | result.x = v.x*scale; 1085 | result.y = v.y*scale; 1086 | result.z = v.z*scale; 1087 | } 1088 | else if (length > max) 1089 | { 1090 | float scale = max/length; 1091 | result.x = v.x*scale; 1092 | result.y = v.y*scale; 1093 | result.z = v.z*scale; 1094 | } 1095 | } 1096 | 1097 | return result; 1098 | } 1099 | 1100 | // Check whether two given vectors are almost equal 1101 | RMAPI int Vector3Equals(Vector3 p, Vector3 q) 1102 | { 1103 | #if !defined(EPSILON) 1104 | #define EPSILON 0.000001f 1105 | #endif 1106 | 1107 | int result = ((fabsf(p.x - q.x)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(p.x), fabsf(q.x))))) && 1108 | ((fabsf(p.y - q.y)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(p.y), fabsf(q.y))))) && 1109 | ((fabsf(p.z - q.z)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(p.z), fabsf(q.z))))); 1110 | 1111 | return result; 1112 | } 1113 | 1114 | // Compute the direction of a refracted ray 1115 | // v: normalized direction of the incoming ray 1116 | // n: normalized normal vector of the interface of two optical media 1117 | // r: ratio of the refractive index of the medium from where the ray comes 1118 | // to the refractive index of the medium on the other side of the surface 1119 | RMAPI Vector3 Vector3Refract(Vector3 v, Vector3 n, float r) 1120 | { 1121 | Vector3 result = { 0 }; 1122 | 1123 | float dot = v.x*n.x + v.y*n.y + v.z*n.z; 1124 | float d = 1.0f - r*r*(1.0f - dot*dot); 1125 | 1126 | if (d >= 0.0f) 1127 | { 1128 | d = sqrtf(d); 1129 | v.x = r*v.x - (r*dot + d)*n.x; 1130 | v.y = r*v.y - (r*dot + d)*n.y; 1131 | v.z = r*v.z - (r*dot + d)*n.z; 1132 | 1133 | result = v; 1134 | } 1135 | 1136 | return result; 1137 | } 1138 | 1139 | //---------------------------------------------------------------------------------- 1140 | // Module Functions Definition - Matrix math 1141 | //---------------------------------------------------------------------------------- 1142 | 1143 | // Compute matrix determinant 1144 | RMAPI float MatrixDeterminant(Matrix mat) 1145 | { 1146 | float result = 0.0f; 1147 | 1148 | // Cache the matrix values (speed optimization) 1149 | float a00 = mat.m0, a01 = mat.m1, a02 = mat.m2, a03 = mat.m3; 1150 | float a10 = mat.m4, a11 = mat.m5, a12 = mat.m6, a13 = mat.m7; 1151 | float a20 = mat.m8, a21 = mat.m9, a22 = mat.m10, a23 = mat.m11; 1152 | float a30 = mat.m12, a31 = mat.m13, a32 = mat.m14, a33 = mat.m15; 1153 | 1154 | result = a30*a21*a12*a03 - a20*a31*a12*a03 - a30*a11*a22*a03 + a10*a31*a22*a03 + 1155 | a20*a11*a32*a03 - a10*a21*a32*a03 - a30*a21*a02*a13 + a20*a31*a02*a13 + 1156 | a30*a01*a22*a13 - a00*a31*a22*a13 - a20*a01*a32*a13 + a00*a21*a32*a13 + 1157 | a30*a11*a02*a23 - a10*a31*a02*a23 - a30*a01*a12*a23 + a00*a31*a12*a23 + 1158 | a10*a01*a32*a23 - a00*a11*a32*a23 - a20*a11*a02*a33 + a10*a21*a02*a33 + 1159 | a20*a01*a12*a33 - a00*a21*a12*a33 - a10*a01*a22*a33 + a00*a11*a22*a33; 1160 | 1161 | return result; 1162 | } 1163 | 1164 | // Get the trace of the matrix (sum of the values along the diagonal) 1165 | RMAPI float MatrixTrace(Matrix mat) 1166 | { 1167 | float result = (mat.m0 + mat.m5 + mat.m10 + mat.m15); 1168 | 1169 | return result; 1170 | } 1171 | 1172 | // Transposes provided matrix 1173 | RMAPI Matrix MatrixTranspose(Matrix mat) 1174 | { 1175 | Matrix result = { 0 }; 1176 | 1177 | result.m0 = mat.m0; 1178 | result.m1 = mat.m4; 1179 | result.m2 = mat.m8; 1180 | result.m3 = mat.m12; 1181 | result.m4 = mat.m1; 1182 | result.m5 = mat.m5; 1183 | result.m6 = mat.m9; 1184 | result.m7 = mat.m13; 1185 | result.m8 = mat.m2; 1186 | result.m9 = mat.m6; 1187 | result.m10 = mat.m10; 1188 | result.m11 = mat.m14; 1189 | result.m12 = mat.m3; 1190 | result.m13 = mat.m7; 1191 | result.m14 = mat.m11; 1192 | result.m15 = mat.m15; 1193 | 1194 | return result; 1195 | } 1196 | 1197 | // Invert provided matrix 1198 | RMAPI Matrix MatrixInvert(Matrix mat) 1199 | { 1200 | Matrix result = { 0 }; 1201 | 1202 | // Cache the matrix values (speed optimization) 1203 | float a00 = mat.m0, a01 = mat.m1, a02 = mat.m2, a03 = mat.m3; 1204 | float a10 = mat.m4, a11 = mat.m5, a12 = mat.m6, a13 = mat.m7; 1205 | float a20 = mat.m8, a21 = mat.m9, a22 = mat.m10, a23 = mat.m11; 1206 | float a30 = mat.m12, a31 = mat.m13, a32 = mat.m14, a33 = mat.m15; 1207 | 1208 | float b00 = a00*a11 - a01*a10; 1209 | float b01 = a00*a12 - a02*a10; 1210 | float b02 = a00*a13 - a03*a10; 1211 | float b03 = a01*a12 - a02*a11; 1212 | float b04 = a01*a13 - a03*a11; 1213 | float b05 = a02*a13 - a03*a12; 1214 | float b06 = a20*a31 - a21*a30; 1215 | float b07 = a20*a32 - a22*a30; 1216 | float b08 = a20*a33 - a23*a30; 1217 | float b09 = a21*a32 - a22*a31; 1218 | float b10 = a21*a33 - a23*a31; 1219 | float b11 = a22*a33 - a23*a32; 1220 | 1221 | // Calculate the invert determinant (inlined to avoid double-caching) 1222 | float invDet = 1.0f/(b00*b11 - b01*b10 + b02*b09 + b03*b08 - b04*b07 + b05*b06); 1223 | 1224 | result.m0 = (a11*b11 - a12*b10 + a13*b09)*invDet; 1225 | result.m1 = (-a01*b11 + a02*b10 - a03*b09)*invDet; 1226 | result.m2 = (a31*b05 - a32*b04 + a33*b03)*invDet; 1227 | result.m3 = (-a21*b05 + a22*b04 - a23*b03)*invDet; 1228 | result.m4 = (-a10*b11 + a12*b08 - a13*b07)*invDet; 1229 | result.m5 = (a00*b11 - a02*b08 + a03*b07)*invDet; 1230 | result.m6 = (-a30*b05 + a32*b02 - a33*b01)*invDet; 1231 | result.m7 = (a20*b05 - a22*b02 + a23*b01)*invDet; 1232 | result.m8 = (a10*b10 - a11*b08 + a13*b06)*invDet; 1233 | result.m9 = (-a00*b10 + a01*b08 - a03*b06)*invDet; 1234 | result.m10 = (a30*b04 - a31*b02 + a33*b00)*invDet; 1235 | result.m11 = (-a20*b04 + a21*b02 - a23*b00)*invDet; 1236 | result.m12 = (-a10*b09 + a11*b07 - a12*b06)*invDet; 1237 | result.m13 = (a00*b09 - a01*b07 + a02*b06)*invDet; 1238 | result.m14 = (-a30*b03 + a31*b01 - a32*b00)*invDet; 1239 | result.m15 = (a20*b03 - a21*b01 + a22*b00)*invDet; 1240 | 1241 | return result; 1242 | } 1243 | 1244 | // Get identity matrix 1245 | RMAPI Matrix MatrixIdentity(void) 1246 | { 1247 | Matrix result = { 1.0f, 0.0f, 0.0f, 0.0f, 1248 | 0.0f, 1.0f, 0.0f, 0.0f, 1249 | 0.0f, 0.0f, 1.0f, 0.0f, 1250 | 0.0f, 0.0f, 0.0f, 1.0f }; 1251 | 1252 | return result; 1253 | } 1254 | 1255 | // Add two matrices 1256 | RMAPI Matrix MatrixAdd(Matrix left, Matrix right) 1257 | { 1258 | Matrix result = { 0 }; 1259 | 1260 | result.m0 = left.m0 + right.m0; 1261 | result.m1 = left.m1 + right.m1; 1262 | result.m2 = left.m2 + right.m2; 1263 | result.m3 = left.m3 + right.m3; 1264 | result.m4 = left.m4 + right.m4; 1265 | result.m5 = left.m5 + right.m5; 1266 | result.m6 = left.m6 + right.m6; 1267 | result.m7 = left.m7 + right.m7; 1268 | result.m8 = left.m8 + right.m8; 1269 | result.m9 = left.m9 + right.m9; 1270 | result.m10 = left.m10 + right.m10; 1271 | result.m11 = left.m11 + right.m11; 1272 | result.m12 = left.m12 + right.m12; 1273 | result.m13 = left.m13 + right.m13; 1274 | result.m14 = left.m14 + right.m14; 1275 | result.m15 = left.m15 + right.m15; 1276 | 1277 | return result; 1278 | } 1279 | 1280 | // Subtract two matrices (left - right) 1281 | RMAPI Matrix MatrixSubtract(Matrix left, Matrix right) 1282 | { 1283 | Matrix result = { 0 }; 1284 | 1285 | result.m0 = left.m0 - right.m0; 1286 | result.m1 = left.m1 - right.m1; 1287 | result.m2 = left.m2 - right.m2; 1288 | result.m3 = left.m3 - right.m3; 1289 | result.m4 = left.m4 - right.m4; 1290 | result.m5 = left.m5 - right.m5; 1291 | result.m6 = left.m6 - right.m6; 1292 | result.m7 = left.m7 - right.m7; 1293 | result.m8 = left.m8 - right.m8; 1294 | result.m9 = left.m9 - right.m9; 1295 | result.m10 = left.m10 - right.m10; 1296 | result.m11 = left.m11 - right.m11; 1297 | result.m12 = left.m12 - right.m12; 1298 | result.m13 = left.m13 - right.m13; 1299 | result.m14 = left.m14 - right.m14; 1300 | result.m15 = left.m15 - right.m15; 1301 | 1302 | return result; 1303 | } 1304 | 1305 | // Get two matrix multiplication 1306 | // NOTE: When multiplying matrices... the order matters! 1307 | RMAPI Matrix MatrixMultiply(Matrix left, Matrix right) 1308 | { 1309 | Matrix result = { 0 }; 1310 | 1311 | result.m0 = left.m0*right.m0 + left.m1*right.m4 + left.m2*right.m8 + left.m3*right.m12; 1312 | result.m1 = left.m0*right.m1 + left.m1*right.m5 + left.m2*right.m9 + left.m3*right.m13; 1313 | result.m2 = left.m0*right.m2 + left.m1*right.m6 + left.m2*right.m10 + left.m3*right.m14; 1314 | result.m3 = left.m0*right.m3 + left.m1*right.m7 + left.m2*right.m11 + left.m3*right.m15; 1315 | result.m4 = left.m4*right.m0 + left.m5*right.m4 + left.m6*right.m8 + left.m7*right.m12; 1316 | result.m5 = left.m4*right.m1 + left.m5*right.m5 + left.m6*right.m9 + left.m7*right.m13; 1317 | result.m6 = left.m4*right.m2 + left.m5*right.m6 + left.m6*right.m10 + left.m7*right.m14; 1318 | result.m7 = left.m4*right.m3 + left.m5*right.m7 + left.m6*right.m11 + left.m7*right.m15; 1319 | result.m8 = left.m8*right.m0 + left.m9*right.m4 + left.m10*right.m8 + left.m11*right.m12; 1320 | result.m9 = left.m8*right.m1 + left.m9*right.m5 + left.m10*right.m9 + left.m11*right.m13; 1321 | result.m10 = left.m8*right.m2 + left.m9*right.m6 + left.m10*right.m10 + left.m11*right.m14; 1322 | result.m11 = left.m8*right.m3 + left.m9*right.m7 + left.m10*right.m11 + left.m11*right.m15; 1323 | result.m12 = left.m12*right.m0 + left.m13*right.m4 + left.m14*right.m8 + left.m15*right.m12; 1324 | result.m13 = left.m12*right.m1 + left.m13*right.m5 + left.m14*right.m9 + left.m15*right.m13; 1325 | result.m14 = left.m12*right.m2 + left.m13*right.m6 + left.m14*right.m10 + left.m15*right.m14; 1326 | result.m15 = left.m12*right.m3 + left.m13*right.m7 + left.m14*right.m11 + left.m15*right.m15; 1327 | 1328 | return result; 1329 | } 1330 | 1331 | // Get translation matrix 1332 | RMAPI Matrix MatrixTranslate(float x, float y, float z) 1333 | { 1334 | Matrix result = { 1.0f, 0.0f, 0.0f, x, 1335 | 0.0f, 1.0f, 0.0f, y, 1336 | 0.0f, 0.0f, 1.0f, z, 1337 | 0.0f, 0.0f, 0.0f, 1.0f }; 1338 | 1339 | return result; 1340 | } 1341 | 1342 | // Create rotation matrix from axis and angle 1343 | // NOTE: Angle should be provided in radians 1344 | RMAPI Matrix MatrixRotate(Vector3 axis, float angle) 1345 | { 1346 | Matrix result = { 0 }; 1347 | 1348 | float x = axis.x, y = axis.y, z = axis.z; 1349 | 1350 | float lengthSquared = x*x + y*y + z*z; 1351 | 1352 | if ((lengthSquared != 1.0f) && (lengthSquared != 0.0f)) 1353 | { 1354 | float ilength = 1.0f/sqrtf(lengthSquared); 1355 | x *= ilength; 1356 | y *= ilength; 1357 | z *= ilength; 1358 | } 1359 | 1360 | float sinres = sinf(angle); 1361 | float cosres = cosf(angle); 1362 | float t = 1.0f - cosres; 1363 | 1364 | result.m0 = x*x*t + cosres; 1365 | result.m1 = y*x*t + z*sinres; 1366 | result.m2 = z*x*t - y*sinres; 1367 | result.m3 = 0.0f; 1368 | 1369 | result.m4 = x*y*t - z*sinres; 1370 | result.m5 = y*y*t + cosres; 1371 | result.m6 = z*y*t + x*sinres; 1372 | result.m7 = 0.0f; 1373 | 1374 | result.m8 = x*z*t + y*sinres; 1375 | result.m9 = y*z*t - x*sinres; 1376 | result.m10 = z*z*t + cosres; 1377 | result.m11 = 0.0f; 1378 | 1379 | result.m12 = 0.0f; 1380 | result.m13 = 0.0f; 1381 | result.m14 = 0.0f; 1382 | result.m15 = 1.0f; 1383 | 1384 | return result; 1385 | } 1386 | 1387 | // Get x-rotation matrix 1388 | // NOTE: Angle must be provided in radians 1389 | RMAPI Matrix MatrixRotateX(float angle) 1390 | { 1391 | Matrix result = { 1.0f, 0.0f, 0.0f, 0.0f, 1392 | 0.0f, 1.0f, 0.0f, 0.0f, 1393 | 0.0f, 0.0f, 1.0f, 0.0f, 1394 | 0.0f, 0.0f, 0.0f, 1.0f }; // MatrixIdentity() 1395 | 1396 | float cosres = cosf(angle); 1397 | float sinres = sinf(angle); 1398 | 1399 | result.m5 = cosres; 1400 | result.m6 = sinres; 1401 | result.m9 = -sinres; 1402 | result.m10 = cosres; 1403 | 1404 | return result; 1405 | } 1406 | 1407 | // Get y-rotation matrix 1408 | // NOTE: Angle must be provided in radians 1409 | RMAPI Matrix MatrixRotateY(float angle) 1410 | { 1411 | Matrix result = { 1.0f, 0.0f, 0.0f, 0.0f, 1412 | 0.0f, 1.0f, 0.0f, 0.0f, 1413 | 0.0f, 0.0f, 1.0f, 0.0f, 1414 | 0.0f, 0.0f, 0.0f, 1.0f }; // MatrixIdentity() 1415 | 1416 | float cosres = cosf(angle); 1417 | float sinres = sinf(angle); 1418 | 1419 | result.m0 = cosres; 1420 | result.m2 = -sinres; 1421 | result.m8 = sinres; 1422 | result.m10 = cosres; 1423 | 1424 | return result; 1425 | } 1426 | 1427 | // Get z-rotation matrix 1428 | // NOTE: Angle must be provided in radians 1429 | RMAPI Matrix MatrixRotateZ(float angle) 1430 | { 1431 | Matrix result = { 1.0f, 0.0f, 0.0f, 0.0f, 1432 | 0.0f, 1.0f, 0.0f, 0.0f, 1433 | 0.0f, 0.0f, 1.0f, 0.0f, 1434 | 0.0f, 0.0f, 0.0f, 1.0f }; // MatrixIdentity() 1435 | 1436 | float cosres = cosf(angle); 1437 | float sinres = sinf(angle); 1438 | 1439 | result.m0 = cosres; 1440 | result.m1 = sinres; 1441 | result.m4 = -sinres; 1442 | result.m5 = cosres; 1443 | 1444 | return result; 1445 | } 1446 | 1447 | 1448 | // Get xyz-rotation matrix 1449 | // NOTE: Angle must be provided in radians 1450 | RMAPI Matrix MatrixRotateXYZ(Vector3 angle) 1451 | { 1452 | Matrix result = { 1.0f, 0.0f, 0.0f, 0.0f, 1453 | 0.0f, 1.0f, 0.0f, 0.0f, 1454 | 0.0f, 0.0f, 1.0f, 0.0f, 1455 | 0.0f, 0.0f, 0.0f, 1.0f }; // MatrixIdentity() 1456 | 1457 | float cosz = cosf(-angle.z); 1458 | float sinz = sinf(-angle.z); 1459 | float cosy = cosf(-angle.y); 1460 | float siny = sinf(-angle.y); 1461 | float cosx = cosf(-angle.x); 1462 | float sinx = sinf(-angle.x); 1463 | 1464 | result.m0 = cosz*cosy; 1465 | result.m1 = (cosz*siny*sinx) - (sinz*cosx); 1466 | result.m2 = (cosz*siny*cosx) + (sinz*sinx); 1467 | 1468 | result.m4 = sinz*cosy; 1469 | result.m5 = (sinz*siny*sinx) + (cosz*cosx); 1470 | result.m6 = (sinz*siny*cosx) - (cosz*sinx); 1471 | 1472 | result.m8 = -siny; 1473 | result.m9 = cosy*sinx; 1474 | result.m10= cosy*cosx; 1475 | 1476 | return result; 1477 | } 1478 | 1479 | // Get zyx-rotation matrix 1480 | // NOTE: Angle must be provided in radians 1481 | RMAPI Matrix MatrixRotateZYX(Vector3 angle) 1482 | { 1483 | Matrix result = { 0 }; 1484 | 1485 | float cz = cosf(angle.z); 1486 | float sz = sinf(angle.z); 1487 | float cy = cosf(angle.y); 1488 | float sy = sinf(angle.y); 1489 | float cx = cosf(angle.x); 1490 | float sx = sinf(angle.x); 1491 | 1492 | result.m0 = cz*cy; 1493 | result.m4 = cz*sy*sx - cx*sz; 1494 | result.m8 = sz*sx + cz*cx*sy; 1495 | result.m12 = 0; 1496 | 1497 | result.m1 = cy*sz; 1498 | result.m5 = cz*cx + sz*sy*sx; 1499 | result.m9 = cx*sz*sy - cz*sx; 1500 | result.m13 = 0; 1501 | 1502 | result.m2 = -sy; 1503 | result.m6 = cy*sx; 1504 | result.m10 = cy*cx; 1505 | result.m14 = 0; 1506 | 1507 | result.m3 = 0; 1508 | result.m7 = 0; 1509 | result.m11 = 0; 1510 | result.m15 = 1; 1511 | 1512 | return result; 1513 | } 1514 | 1515 | // Get scaling matrix 1516 | RMAPI Matrix MatrixScale(float x, float y, float z) 1517 | { 1518 | Matrix result = { x, 0.0f, 0.0f, 0.0f, 1519 | 0.0f, y, 0.0f, 0.0f, 1520 | 0.0f, 0.0f, z, 0.0f, 1521 | 0.0f, 0.0f, 0.0f, 1.0f }; 1522 | 1523 | return result; 1524 | } 1525 | 1526 | // Get perspective projection matrix 1527 | RMAPI Matrix MatrixFrustum(double left, double right, double bottom, double top, double near, double far) 1528 | { 1529 | Matrix result = { 0 }; 1530 | 1531 | float rl = (float)(right - left); 1532 | float tb = (float)(top - bottom); 1533 | float fn = (float)(far - near); 1534 | 1535 | result.m0 = ((float)near*2.0f)/rl; 1536 | result.m1 = 0.0f; 1537 | result.m2 = 0.0f; 1538 | result.m3 = 0.0f; 1539 | 1540 | result.m4 = 0.0f; 1541 | result.m5 = ((float)near*2.0f)/tb; 1542 | result.m6 = 0.0f; 1543 | result.m7 = 0.0f; 1544 | 1545 | result.m8 = ((float)right + (float)left)/rl; 1546 | result.m9 = ((float)top + (float)bottom)/tb; 1547 | result.m10 = -((float)far + (float)near)/fn; 1548 | result.m11 = -1.0f; 1549 | 1550 | result.m12 = 0.0f; 1551 | result.m13 = 0.0f; 1552 | result.m14 = -((float)far*(float)near*2.0f)/fn; 1553 | result.m15 = 0.0f; 1554 | 1555 | return result; 1556 | } 1557 | 1558 | // Get perspective projection matrix 1559 | // NOTE: Fovy angle must be provided in radians 1560 | RMAPI Matrix MatrixPerspective(double fovY, double aspect, double nearPlane, double farPlane) 1561 | { 1562 | Matrix result = { 0 }; 1563 | 1564 | double top = nearPlane*tan(fovY*0.5); 1565 | double bottom = -top; 1566 | double right = top*aspect; 1567 | double left = -right; 1568 | 1569 | // MatrixFrustum(-right, right, -top, top, near, far); 1570 | float rl = (float)(right - left); 1571 | float tb = (float)(top - bottom); 1572 | float fn = (float)(farPlane - nearPlane); 1573 | 1574 | result.m0 = ((float)nearPlane*2.0f)/rl; 1575 | result.m5 = ((float)nearPlane*2.0f)/tb; 1576 | result.m8 = ((float)right + (float)left)/rl; 1577 | result.m9 = ((float)top + (float)bottom)/tb; 1578 | result.m10 = -((float)farPlane + (float)nearPlane)/fn; 1579 | result.m11 = -1.0f; 1580 | result.m14 = -((float)farPlane*(float)nearPlane*2.0f)/fn; 1581 | 1582 | return result; 1583 | } 1584 | 1585 | // Get orthographic projection matrix 1586 | RMAPI Matrix MatrixOrtho(double left, double right, double bottom, double top, double nearPlane, double farPlane) 1587 | { 1588 | Matrix result = { 0 }; 1589 | 1590 | float rl = (float)(right - left); 1591 | float tb = (float)(top - bottom); 1592 | float fn = (float)(farPlane - nearPlane); 1593 | 1594 | result.m0 = 2.0f/rl; 1595 | result.m1 = 0.0f; 1596 | result.m2 = 0.0f; 1597 | result.m3 = 0.0f; 1598 | result.m4 = 0.0f; 1599 | result.m5 = 2.0f/tb; 1600 | result.m6 = 0.0f; 1601 | result.m7 = 0.0f; 1602 | result.m8 = 0.0f; 1603 | result.m9 = 0.0f; 1604 | result.m10 = -2.0f/fn; 1605 | result.m11 = 0.0f; 1606 | result.m12 = -((float)left + (float)right)/rl; 1607 | result.m13 = -((float)top + (float)bottom)/tb; 1608 | result.m14 = -((float)farPlane + (float)nearPlane)/fn; 1609 | result.m15 = 1.0f; 1610 | 1611 | return result; 1612 | } 1613 | 1614 | // Get camera look-at matrix (view matrix) 1615 | RMAPI Matrix MatrixLookAt(Vector3 eye, Vector3 target, Vector3 up) 1616 | { 1617 | Matrix result = { 0 }; 1618 | 1619 | float length = 0.0f; 1620 | float ilength = 0.0f; 1621 | 1622 | // Vector3Subtract(eye, target) 1623 | Vector3 vz = { eye.x - target.x, eye.y - target.y, eye.z - target.z }; 1624 | 1625 | // Vector3Normalize(vz) 1626 | Vector3 v = vz; 1627 | length = sqrtf(v.x*v.x + v.y*v.y + v.z*v.z); 1628 | if (length == 0.0f) length = 1.0f; 1629 | ilength = 1.0f/length; 1630 | vz.x *= ilength; 1631 | vz.y *= ilength; 1632 | vz.z *= ilength; 1633 | 1634 | // Vector3CrossProduct(up, vz) 1635 | Vector3 vx = { up.y*vz.z - up.z*vz.y, up.z*vz.x - up.x*vz.z, up.x*vz.y - up.y*vz.x }; 1636 | 1637 | // Vector3Normalize(x) 1638 | v = vx; 1639 | length = sqrtf(v.x*v.x + v.y*v.y + v.z*v.z); 1640 | if (length == 0.0f) length = 1.0f; 1641 | ilength = 1.0f/length; 1642 | vx.x *= ilength; 1643 | vx.y *= ilength; 1644 | vx.z *= ilength; 1645 | 1646 | // Vector3CrossProduct(vz, vx) 1647 | Vector3 vy = { vz.y*vx.z - vz.z*vx.y, vz.z*vx.x - vz.x*vx.z, vz.x*vx.y - vz.y*vx.x }; 1648 | 1649 | result.m0 = vx.x; 1650 | result.m1 = vy.x; 1651 | result.m2 = vz.x; 1652 | result.m3 = 0.0f; 1653 | result.m4 = vx.y; 1654 | result.m5 = vy.y; 1655 | result.m6 = vz.y; 1656 | result.m7 = 0.0f; 1657 | result.m8 = vx.z; 1658 | result.m9 = vy.z; 1659 | result.m10 = vz.z; 1660 | result.m11 = 0.0f; 1661 | result.m12 = -(vx.x*eye.x + vx.y*eye.y + vx.z*eye.z); // Vector3DotProduct(vx, eye) 1662 | result.m13 = -(vy.x*eye.x + vy.y*eye.y + vy.z*eye.z); // Vector3DotProduct(vy, eye) 1663 | result.m14 = -(vz.x*eye.x + vz.y*eye.y + vz.z*eye.z); // Vector3DotProduct(vz, eye) 1664 | result.m15 = 1.0f; 1665 | 1666 | return result; 1667 | } 1668 | 1669 | // Get float array of matrix data 1670 | RMAPI float16 MatrixToFloatV(Matrix mat) 1671 | { 1672 | float16 result = { 0 }; 1673 | 1674 | result.v[0] = mat.m0; 1675 | result.v[1] = mat.m1; 1676 | result.v[2] = mat.m2; 1677 | result.v[3] = mat.m3; 1678 | result.v[4] = mat.m4; 1679 | result.v[5] = mat.m5; 1680 | result.v[6] = mat.m6; 1681 | result.v[7] = mat.m7; 1682 | result.v[8] = mat.m8; 1683 | result.v[9] = mat.m9; 1684 | result.v[10] = mat.m10; 1685 | result.v[11] = mat.m11; 1686 | result.v[12] = mat.m12; 1687 | result.v[13] = mat.m13; 1688 | result.v[14] = mat.m14; 1689 | result.v[15] = mat.m15; 1690 | 1691 | return result; 1692 | } 1693 | 1694 | //---------------------------------------------------------------------------------- 1695 | // Module Functions Definition - Quaternion math 1696 | //---------------------------------------------------------------------------------- 1697 | 1698 | // Add two quaternions 1699 | RMAPI Quaternion QuaternionAdd(Quaternion q1, Quaternion q2) 1700 | { 1701 | Quaternion result = {q1.x + q2.x, q1.y + q2.y, q1.z + q2.z, q1.w + q2.w}; 1702 | 1703 | return result; 1704 | } 1705 | 1706 | // Add quaternion and float value 1707 | RMAPI Quaternion QuaternionAddValue(Quaternion q, float add) 1708 | { 1709 | Quaternion result = {q.x + add, q.y + add, q.z + add, q.w + add}; 1710 | 1711 | return result; 1712 | } 1713 | 1714 | // Subtract two quaternions 1715 | RMAPI Quaternion QuaternionSubtract(Quaternion q1, Quaternion q2) 1716 | { 1717 | Quaternion result = {q1.x - q2.x, q1.y - q2.y, q1.z - q2.z, q1.w - q2.w}; 1718 | 1719 | return result; 1720 | } 1721 | 1722 | // Subtract quaternion and float value 1723 | RMAPI Quaternion QuaternionSubtractValue(Quaternion q, float sub) 1724 | { 1725 | Quaternion result = {q.x - sub, q.y - sub, q.z - sub, q.w - sub}; 1726 | 1727 | return result; 1728 | } 1729 | 1730 | // Get identity quaternion 1731 | RMAPI Quaternion QuaternionIdentity(void) 1732 | { 1733 | Quaternion result = { 0.0f, 0.0f, 0.0f, 1.0f }; 1734 | 1735 | return result; 1736 | } 1737 | 1738 | // Computes the length of a quaternion 1739 | RMAPI float QuaternionLength(Quaternion q) 1740 | { 1741 | float result = sqrtf(q.x*q.x + q.y*q.y + q.z*q.z + q.w*q.w); 1742 | 1743 | return result; 1744 | } 1745 | 1746 | // Normalize provided quaternion 1747 | RMAPI Quaternion QuaternionNormalize(Quaternion q) 1748 | { 1749 | Quaternion result = { 0 }; 1750 | 1751 | float length = sqrtf(q.x*q.x + q.y*q.y + q.z*q.z + q.w*q.w); 1752 | if (length == 0.0f) length = 1.0f; 1753 | float ilength = 1.0f/length; 1754 | 1755 | result.x = q.x*ilength; 1756 | result.y = q.y*ilength; 1757 | result.z = q.z*ilength; 1758 | result.w = q.w*ilength; 1759 | 1760 | return result; 1761 | } 1762 | 1763 | // Invert provided quaternion 1764 | RMAPI Quaternion QuaternionInvert(Quaternion q) 1765 | { 1766 | Quaternion result = q; 1767 | 1768 | float lengthSq = q.x*q.x + q.y*q.y + q.z*q.z + q.w*q.w; 1769 | 1770 | if (lengthSq != 0.0f) 1771 | { 1772 | float invLength = 1.0f/lengthSq; 1773 | 1774 | result.x *= -invLength; 1775 | result.y *= -invLength; 1776 | result.z *= -invLength; 1777 | result.w *= invLength; 1778 | } 1779 | 1780 | return result; 1781 | } 1782 | 1783 | // Calculate two quaternion multiplication 1784 | RMAPI Quaternion QuaternionMultiply(Quaternion q1, Quaternion q2) 1785 | { 1786 | Quaternion result = { 0 }; 1787 | 1788 | float qax = q1.x, qay = q1.y, qaz = q1.z, qaw = q1.w; 1789 | float qbx = q2.x, qby = q2.y, qbz = q2.z, qbw = q2.w; 1790 | 1791 | result.x = qax*qbw + qaw*qbx + qay*qbz - qaz*qby; 1792 | result.y = qay*qbw + qaw*qby + qaz*qbx - qax*qbz; 1793 | result.z = qaz*qbw + qaw*qbz + qax*qby - qay*qbx; 1794 | result.w = qaw*qbw - qax*qbx - qay*qby - qaz*qbz; 1795 | 1796 | return result; 1797 | } 1798 | 1799 | // Scale quaternion by float value 1800 | RMAPI Quaternion QuaternionScale(Quaternion q, float mul) 1801 | { 1802 | Quaternion result = { 0 }; 1803 | 1804 | result.x = q.x*mul; 1805 | result.y = q.y*mul; 1806 | result.z = q.z*mul; 1807 | result.w = q.w*mul; 1808 | 1809 | return result; 1810 | } 1811 | 1812 | // Divide two quaternions 1813 | RMAPI Quaternion QuaternionDivide(Quaternion q1, Quaternion q2) 1814 | { 1815 | Quaternion result = { q1.x/q2.x, q1.y/q2.y, q1.z/q2.z, q1.w/q2.w }; 1816 | 1817 | return result; 1818 | } 1819 | 1820 | // Calculate linear interpolation between two quaternions 1821 | RMAPI Quaternion QuaternionLerp(Quaternion q1, Quaternion q2, float amount) 1822 | { 1823 | Quaternion result = { 0 }; 1824 | 1825 | result.x = q1.x + amount*(q2.x - q1.x); 1826 | result.y = q1.y + amount*(q2.y - q1.y); 1827 | result.z = q1.z + amount*(q2.z - q1.z); 1828 | result.w = q1.w + amount*(q2.w - q1.w); 1829 | 1830 | return result; 1831 | } 1832 | 1833 | // Calculate slerp-optimized interpolation between two quaternions 1834 | RMAPI Quaternion QuaternionNlerp(Quaternion q1, Quaternion q2, float amount) 1835 | { 1836 | Quaternion result = { 0 }; 1837 | 1838 | // QuaternionLerp(q1, q2, amount) 1839 | result.x = q1.x + amount*(q2.x - q1.x); 1840 | result.y = q1.y + amount*(q2.y - q1.y); 1841 | result.z = q1.z + amount*(q2.z - q1.z); 1842 | result.w = q1.w + amount*(q2.w - q1.w); 1843 | 1844 | // QuaternionNormalize(q); 1845 | Quaternion q = result; 1846 | float length = sqrtf(q.x*q.x + q.y*q.y + q.z*q.z + q.w*q.w); 1847 | if (length == 0.0f) length = 1.0f; 1848 | float ilength = 1.0f/length; 1849 | 1850 | result.x = q.x*ilength; 1851 | result.y = q.y*ilength; 1852 | result.z = q.z*ilength; 1853 | result.w = q.w*ilength; 1854 | 1855 | return result; 1856 | } 1857 | 1858 | // Calculates spherical linear interpolation between two quaternions 1859 | RMAPI Quaternion QuaternionSlerp(Quaternion q1, Quaternion q2, float amount) 1860 | { 1861 | Quaternion result = { 0 }; 1862 | 1863 | #if !defined(EPSILON) 1864 | #define EPSILON 0.000001f 1865 | #endif 1866 | 1867 | float cosHalfTheta = q1.x*q2.x + q1.y*q2.y + q1.z*q2.z + q1.w*q2.w; 1868 | 1869 | if (cosHalfTheta < 0) 1870 | { 1871 | q2.x = -q2.x; q2.y = -q2.y; q2.z = -q2.z; q2.w = -q2.w; 1872 | cosHalfTheta = -cosHalfTheta; 1873 | } 1874 | 1875 | if (fabsf(cosHalfTheta) >= 1.0f) result = q1; 1876 | else if (cosHalfTheta > 0.95f) result = QuaternionNlerp(q1, q2, amount); 1877 | else 1878 | { 1879 | float halfTheta = acosf(cosHalfTheta); 1880 | float sinHalfTheta = sqrtf(1.0f - cosHalfTheta*cosHalfTheta); 1881 | 1882 | if (fabsf(sinHalfTheta) < EPSILON) 1883 | { 1884 | result.x = (q1.x*0.5f + q2.x*0.5f); 1885 | result.y = (q1.y*0.5f + q2.y*0.5f); 1886 | result.z = (q1.z*0.5f + q2.z*0.5f); 1887 | result.w = (q1.w*0.5f + q2.w*0.5f); 1888 | } 1889 | else 1890 | { 1891 | float ratioA = sinf((1 - amount)*halfTheta)/sinHalfTheta; 1892 | float ratioB = sinf(amount*halfTheta)/sinHalfTheta; 1893 | 1894 | result.x = (q1.x*ratioA + q2.x*ratioB); 1895 | result.y = (q1.y*ratioA + q2.y*ratioB); 1896 | result.z = (q1.z*ratioA + q2.z*ratioB); 1897 | result.w = (q1.w*ratioA + q2.w*ratioB); 1898 | } 1899 | } 1900 | 1901 | return result; 1902 | } 1903 | 1904 | // Calculate quaternion based on the rotation from one vector to another 1905 | RMAPI Quaternion QuaternionFromVector3ToVector3(Vector3 from, Vector3 to) 1906 | { 1907 | Quaternion result = { 0 }; 1908 | 1909 | float cos2Theta = (from.x*to.x + from.y*to.y + from.z*to.z); // Vector3DotProduct(from, to) 1910 | Vector3 cross = { from.y*to.z - from.z*to.y, from.z*to.x - from.x*to.z, from.x*to.y - from.y*to.x }; // Vector3CrossProduct(from, to) 1911 | 1912 | result.x = cross.x; 1913 | result.y = cross.y; 1914 | result.z = cross.z; 1915 | result.w = 1.0f + cos2Theta; 1916 | 1917 | // QuaternionNormalize(q); 1918 | // NOTE: Normalize to essentially nlerp the original and identity to 0.5 1919 | Quaternion q = result; 1920 | float length = sqrtf(q.x*q.x + q.y*q.y + q.z*q.z + q.w*q.w); 1921 | if (length == 0.0f) length = 1.0f; 1922 | float ilength = 1.0f/length; 1923 | 1924 | result.x = q.x*ilength; 1925 | result.y = q.y*ilength; 1926 | result.z = q.z*ilength; 1927 | result.w = q.w*ilength; 1928 | 1929 | return result; 1930 | } 1931 | 1932 | // Get a quaternion for a given rotation matrix 1933 | RMAPI Quaternion QuaternionFromMatrix(Matrix mat) 1934 | { 1935 | Quaternion result = { 0 }; 1936 | 1937 | float fourWSquaredMinus1 = mat.m0 + mat.m5 + mat.m10; 1938 | float fourXSquaredMinus1 = mat.m0 - mat.m5 - mat.m10; 1939 | float fourYSquaredMinus1 = mat.m5 - mat.m0 - mat.m10; 1940 | float fourZSquaredMinus1 = mat.m10 - mat.m0 - mat.m5; 1941 | 1942 | int biggestIndex = 0; 1943 | float fourBiggestSquaredMinus1 = fourWSquaredMinus1; 1944 | if (fourXSquaredMinus1 > fourBiggestSquaredMinus1) 1945 | { 1946 | fourBiggestSquaredMinus1 = fourXSquaredMinus1; 1947 | biggestIndex = 1; 1948 | } 1949 | 1950 | if (fourYSquaredMinus1 > fourBiggestSquaredMinus1) 1951 | { 1952 | fourBiggestSquaredMinus1 = fourYSquaredMinus1; 1953 | biggestIndex = 2; 1954 | } 1955 | 1956 | if (fourZSquaredMinus1 > fourBiggestSquaredMinus1) 1957 | { 1958 | fourBiggestSquaredMinus1 = fourZSquaredMinus1; 1959 | biggestIndex = 3; 1960 | } 1961 | 1962 | float biggestVal = sqrtf(fourBiggestSquaredMinus1 + 1.0f)*0.5f; 1963 | float mult = 0.25f / biggestVal; 1964 | 1965 | switch (biggestIndex) 1966 | { 1967 | case 0: 1968 | result.w = biggestVal; 1969 | result.x = (mat.m6 - mat.m9)*mult; 1970 | result.y = (mat.m8 - mat.m2)*mult; 1971 | result.z = (mat.m1 - mat.m4)*mult; 1972 | break; 1973 | case 1: 1974 | result.x = biggestVal; 1975 | result.w = (mat.m6 - mat.m9)*mult; 1976 | result.y = (mat.m1 + mat.m4)*mult; 1977 | result.z = (mat.m8 + mat.m2)*mult; 1978 | break; 1979 | case 2: 1980 | result.y = biggestVal; 1981 | result.w = (mat.m8 - mat.m2)*mult; 1982 | result.x = (mat.m1 + mat.m4)*mult; 1983 | result.z = (mat.m6 + mat.m9)*mult; 1984 | break; 1985 | case 3: 1986 | result.z = biggestVal; 1987 | result.w = (mat.m1 - mat.m4)*mult; 1988 | result.x = (mat.m8 + mat.m2)*mult; 1989 | result.y = (mat.m6 + mat.m9)*mult; 1990 | break; 1991 | } 1992 | 1993 | return result; 1994 | } 1995 | 1996 | // Get a matrix for a given quaternion 1997 | RMAPI Matrix QuaternionToMatrix(Quaternion q) 1998 | { 1999 | Matrix result = { 1.0f, 0.0f, 0.0f, 0.0f, 2000 | 0.0f, 1.0f, 0.0f, 0.0f, 2001 | 0.0f, 0.0f, 1.0f, 0.0f, 2002 | 0.0f, 0.0f, 0.0f, 1.0f }; // MatrixIdentity() 2003 | 2004 | float a2 = q.x*q.x; 2005 | float b2 = q.y*q.y; 2006 | float c2 = q.z*q.z; 2007 | float ac = q.x*q.z; 2008 | float ab = q.x*q.y; 2009 | float bc = q.y*q.z; 2010 | float ad = q.w*q.x; 2011 | float bd = q.w*q.y; 2012 | float cd = q.w*q.z; 2013 | 2014 | result.m0 = 1 - 2*(b2 + c2); 2015 | result.m1 = 2*(ab + cd); 2016 | result.m2 = 2*(ac - bd); 2017 | 2018 | result.m4 = 2*(ab - cd); 2019 | result.m5 = 1 - 2*(a2 + c2); 2020 | result.m6 = 2*(bc + ad); 2021 | 2022 | result.m8 = 2*(ac + bd); 2023 | result.m9 = 2*(bc - ad); 2024 | result.m10 = 1 - 2*(a2 + b2); 2025 | 2026 | return result; 2027 | } 2028 | 2029 | // Get rotation quaternion for an angle and axis 2030 | // NOTE: Angle must be provided in radians 2031 | RMAPI Quaternion QuaternionFromAxisAngle(Vector3 axis, float angle) 2032 | { 2033 | Quaternion result = { 0.0f, 0.0f, 0.0f, 1.0f }; 2034 | 2035 | float axisLength = sqrtf(axis.x*axis.x + axis.y*axis.y + axis.z*axis.z); 2036 | 2037 | if (axisLength != 0.0f) 2038 | { 2039 | angle *= 0.5f; 2040 | 2041 | float length = 0.0f; 2042 | float ilength = 0.0f; 2043 | 2044 | // Vector3Normalize(axis) 2045 | Vector3 v = axis; 2046 | length = sqrtf(v.x*v.x + v.y*v.y + v.z*v.z); 2047 | if (length == 0.0f) length = 1.0f; 2048 | ilength = 1.0f/length; 2049 | axis.x *= ilength; 2050 | axis.y *= ilength; 2051 | axis.z *= ilength; 2052 | 2053 | float sinres = sinf(angle); 2054 | float cosres = cosf(angle); 2055 | 2056 | result.x = axis.x*sinres; 2057 | result.y = axis.y*sinres; 2058 | result.z = axis.z*sinres; 2059 | result.w = cosres; 2060 | 2061 | // QuaternionNormalize(q); 2062 | Quaternion q = result; 2063 | length = sqrtf(q.x*q.x + q.y*q.y + q.z*q.z + q.w*q.w); 2064 | if (length == 0.0f) length = 1.0f; 2065 | ilength = 1.0f/length; 2066 | result.x = q.x*ilength; 2067 | result.y = q.y*ilength; 2068 | result.z = q.z*ilength; 2069 | result.w = q.w*ilength; 2070 | } 2071 | 2072 | return result; 2073 | } 2074 | 2075 | // Get the rotation angle and axis for a given quaternion 2076 | RMAPI void QuaternionToAxisAngle(Quaternion q, Vector3 *outAxis, float *outAngle) 2077 | { 2078 | if (fabsf(q.w) > 1.0f) 2079 | { 2080 | // QuaternionNormalize(q); 2081 | float length = sqrtf(q.x*q.x + q.y*q.y + q.z*q.z + q.w*q.w); 2082 | if (length == 0.0f) length = 1.0f; 2083 | float ilength = 1.0f/length; 2084 | 2085 | q.x = q.x*ilength; 2086 | q.y = q.y*ilength; 2087 | q.z = q.z*ilength; 2088 | q.w = q.w*ilength; 2089 | } 2090 | 2091 | Vector3 resAxis = { 0.0f, 0.0f, 0.0f }; 2092 | float resAngle = 2.0f*acosf(q.w); 2093 | float den = sqrtf(1.0f - q.w*q.w); 2094 | 2095 | if (den > EPSILON) 2096 | { 2097 | resAxis.x = q.x/den; 2098 | resAxis.y = q.y/den; 2099 | resAxis.z = q.z/den; 2100 | } 2101 | else 2102 | { 2103 | // This occurs when the angle is zero. 2104 | // Not a problem: just set an arbitrary normalized axis. 2105 | resAxis.x = 1.0f; 2106 | } 2107 | 2108 | *outAxis = resAxis; 2109 | *outAngle = resAngle; 2110 | } 2111 | 2112 | // Get the quaternion equivalent to Euler angles 2113 | // NOTE: Rotation order is ZYX 2114 | RMAPI Quaternion QuaternionFromEuler(float pitch, float yaw, float roll) 2115 | { 2116 | Quaternion result = { 0 }; 2117 | 2118 | float x0 = cosf(pitch*0.5f); 2119 | float x1 = sinf(pitch*0.5f); 2120 | float y0 = cosf(yaw*0.5f); 2121 | float y1 = sinf(yaw*0.5f); 2122 | float z0 = cosf(roll*0.5f); 2123 | float z1 = sinf(roll*0.5f); 2124 | 2125 | result.x = x1*y0*z0 - x0*y1*z1; 2126 | result.y = x0*y1*z0 + x1*y0*z1; 2127 | result.z = x0*y0*z1 - x1*y1*z0; 2128 | result.w = x0*y0*z0 + x1*y1*z1; 2129 | 2130 | return result; 2131 | } 2132 | 2133 | // Get the Euler angles equivalent to quaternion (roll, pitch, yaw) 2134 | // NOTE: Angles are returned in a Vector3 struct in radians 2135 | RMAPI Vector3 QuaternionToEuler(Quaternion q) 2136 | { 2137 | Vector3 result = { 0 }; 2138 | 2139 | // Roll (x-axis rotation) 2140 | float x0 = 2.0f*(q.w*q.x + q.y*q.z); 2141 | float x1 = 1.0f - 2.0f*(q.x*q.x + q.y*q.y); 2142 | result.x = atan2f(x0, x1); 2143 | 2144 | // Pitch (y-axis rotation) 2145 | float y0 = 2.0f*(q.w*q.y - q.z*q.x); 2146 | y0 = y0 > 1.0f ? 1.0f : y0; 2147 | y0 = y0 < -1.0f ? -1.0f : y0; 2148 | result.y = asinf(y0); 2149 | 2150 | // Yaw (z-axis rotation) 2151 | float z0 = 2.0f*(q.w*q.z + q.x*q.y); 2152 | float z1 = 1.0f - 2.0f*(q.y*q.y + q.z*q.z); 2153 | result.z = atan2f(z0, z1); 2154 | 2155 | return result; 2156 | } 2157 | 2158 | // Transform a quaternion given a transformation matrix 2159 | RMAPI Quaternion QuaternionTransform(Quaternion q, Matrix mat) 2160 | { 2161 | Quaternion result = { 0 }; 2162 | 2163 | result.x = mat.m0*q.x + mat.m4*q.y + mat.m8*q.z + mat.m12*q.w; 2164 | result.y = mat.m1*q.x + mat.m5*q.y + mat.m9*q.z + mat.m13*q.w; 2165 | result.z = mat.m2*q.x + mat.m6*q.y + mat.m10*q.z + mat.m14*q.w; 2166 | result.w = mat.m3*q.x + mat.m7*q.y + mat.m11*q.z + mat.m15*q.w; 2167 | 2168 | return result; 2169 | } 2170 | 2171 | // Check whether two given quaternions are almost equal 2172 | RMAPI int QuaternionEquals(Quaternion p, Quaternion q) 2173 | { 2174 | #if !defined(EPSILON) 2175 | #define EPSILON 0.000001f 2176 | #endif 2177 | 2178 | int result = (((fabsf(p.x - q.x)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(p.x), fabsf(q.x))))) && 2179 | ((fabsf(p.y - q.y)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(p.y), fabsf(q.y))))) && 2180 | ((fabsf(p.z - q.z)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(p.z), fabsf(q.z))))) && 2181 | ((fabsf(p.w - q.w)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(p.w), fabsf(q.w)))))) || 2182 | (((fabsf(p.x + q.x)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(p.x), fabsf(q.x))))) && 2183 | ((fabsf(p.y + q.y)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(p.y), fabsf(q.y))))) && 2184 | ((fabsf(p.z + q.z)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(p.z), fabsf(q.z))))) && 2185 | ((fabsf(p.w + q.w)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(p.w), fabsf(q.w)))))); 2186 | 2187 | return result; 2188 | } 2189 | 2190 | #endif // RAYMATH_H 2191 | -------------------------------------------------------------------------------- /src/2d.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | #include 10 | 11 | #define NOB_IMPLEMENTATION 12 | #include "nob.h" 13 | 14 | #define K 5 15 | #define SAMPLE_RADIUS 4.0f 16 | #define MEAN_RADIUS (3*SAMPLE_RADIUS) 17 | 18 | #include "common.c" 19 | 20 | // #define LEAF 21 | 22 | typedef struct { 23 | Vector2 *items; 24 | size_t count; 25 | size_t capacity; 26 | } Samples2D; 27 | 28 | static void generate_cluster(Vector2 center, float radius, size_t count, Samples2D *samples) 29 | { 30 | for (size_t i = 0; i < count; ++i) { 31 | float angle = rand_float()*2*PI; 32 | float mag = rand_float()*radius; 33 | Vector2 sample = { 34 | .x = cosf(angle)*mag, 35 | .y = sinf(angle)*mag, 36 | }; 37 | nob_da_append(samples, Vector2Add(sample, center)); 38 | } 39 | } 40 | 41 | static Samples2D set = {0}; 42 | static Samples2D clusters[K] = {0}; 43 | static Vector2 means[K] = {0}; 44 | 45 | void generate_new_state(float min_x, float max_x, float min_y, float max_y) 46 | { 47 | #ifndef LEAF 48 | set.count = 0; 49 | generate_cluster(CLITERAL(Vector2){0}, 10, 100, &set); 50 | generate_cluster(CLITERAL(Vector2){min_x*0.5f, max_y*0.5f}, 5, 50, &set); 51 | generate_cluster(CLITERAL(Vector2){max_x*0.5f, max_y*0.5f}, 5, 50, &set); 52 | generate_cluster(CLITERAL(Vector2){min_x*0.5f, min_y*0.5f}, 5, 50, &set); 53 | generate_cluster(CLITERAL(Vector2){max_x*0.5f, min_y*0.5f}, 5, 50, &set); 54 | #endif 55 | 56 | for (size_t i = 0; i < K; ++i) { 57 | means[i].x = Lerp(min_x, max_x, rand_float()); 58 | means[i].y = Lerp(min_y, max_y, rand_float()); 59 | } 60 | } 61 | 62 | void recluster_state(void) 63 | { 64 | for (size_t j = 0; j < K; ++j) { 65 | clusters[j].count = 0; 66 | } 67 | for (size_t i = 0; i < set.count; ++i) { 68 | Vector2 p = set.items[i]; 69 | int k = -1; 70 | float s = FLT_MAX; 71 | for (size_t j = 0; j < K; ++j) { 72 | Vector2 m = means[j]; 73 | float sm = Vector2LengthSqr(Vector2Subtract(p, m)); 74 | if (sm < s) { 75 | s = sm; 76 | k = j; 77 | } 78 | } 79 | nob_da_append(&clusters[k], p); 80 | } 81 | } 82 | 83 | void update_means(float min_x, float max_x, float min_y, float max_y) 84 | { 85 | for (size_t i = 0; i < K; ++i) { 86 | if (clusters[i].count > 0) { 87 | means[i] = Vector2Zero(); 88 | for (size_t j = 0; j < clusters[i].count; ++j) { 89 | means[i] = Vector2Add(means[i], clusters[i].items[j]); 90 | } 91 | means[i].x /= clusters[i].count; 92 | means[i].y /= clusters[i].count; 93 | } else { 94 | means[i].x = Lerp(min_x, max_x, rand_float()); 95 | means[i].y = Lerp(min_y, max_y, rand_float()); 96 | } 97 | } 98 | } 99 | 100 | typedef enum { 101 | LEAF_CLASS = 0, 102 | LEAF_SPECIMEN_NUMBER, 103 | LEAF_ECCENTRICITY, 104 | LEAF_ASPECT_RATIO, 105 | LEAF_ELONGATION, 106 | LEAF_SOLIDITY, 107 | LEAF_STOCHASTIC_CONVEXITY, 108 | LEAF_ISOPERIMETRIC_FACTOR, 109 | LEAF_MAXIMAL_INDENTATION_DEPTH, 110 | LEAF_LOBEDNESS, 111 | LEAF_AVERAGE_INTENSITY, 112 | LEAF_AVERAGE_CONTRAST, 113 | LEAF_SMOOTHNESS, 114 | LEAF_THIRD_MOMENT, 115 | LEAF_UNIFORMITY, 116 | LEAF_ENTROPY, 117 | } Leaf_Attr; 118 | 119 | int main(int argc, char **argv) 120 | { 121 | #ifdef LEAF 122 | const char *program = nob_shift_args(&argc, &argv); 123 | 124 | float min_x = FLT_MAX; 125 | float max_x = FLT_MIN; 126 | float min_y = FLT_MAX; 127 | float max_y = FLT_MIN; 128 | 129 | const char *leaf_path = nob_shift_args(&argc, &argv); 130 | Nob_String_Builder sb = {0}; 131 | if (!nob_read_entire_file(leaf_path, &sb)) return 1; 132 | 133 | Nob_String_View content = nob_sv_from_parts(sb.items, sb.count); 134 | while (content.count > 0) { 135 | Nob_String_View line = nob_sv_chop_by_delim(&content, '\n'); 136 | Vector2 p = {0}; 137 | for (size_t i = 0; line.count > 0; ++i) { 138 | Nob_String_View attr = nob_sv_chop_by_delim(&line, ','); 139 | float value = strtof(nob_temp_sprintf(SV_Fmt, SV_Arg(attr)), NULL); 140 | switch (i) { 141 | case LEAF_ENTROPY: p.x = value; break; 142 | case LEAF_ELONGATION: p.y = value; break; 143 | default: {} 144 | } 145 | } 146 | nob_da_append(&set, p); 147 | if (p.x < min_x) min_x = p.x; 148 | if (p.x > max_x) max_x = p.x; 149 | if (p.y < min_y) min_y = p.y; 150 | if (p.y > max_y) max_y = p.y; 151 | } 152 | nob_temp_reset(); 153 | #else 154 | float min_x = -20; 155 | float max_x = 20; 156 | float min_y = -20; 157 | float max_y = 20; 158 | #endif 159 | 160 | nob_log(NOB_INFO, "x = %f..%f", min_x, max_x); 161 | nob_log(NOB_INFO, "y = %f..%f", min_y, max_y); 162 | 163 | srand(time(0)); 164 | SetConfigFlags(FLAG_WINDOW_RESIZABLE); 165 | InitWindow(800, 600, "K-means"); 166 | if (!IsWindowReady()) return 1; 167 | 168 | generate_new_state(min_x, max_x, min_y, max_y); 169 | recluster_state(); 170 | 171 | float zoom = 1.0f; 172 | float zoom_vel = 0.0f; 173 | Vector2 position = {0}; 174 | while (!WindowShouldClose()) { 175 | float w = GetScreenWidth(); 176 | float h = GetScreenHeight(); 177 | float dt = GetFrameTime(); 178 | 179 | if (IsKeyPressed(KEY_R)) { 180 | generate_new_state(min_x, max_x, min_y, max_y); 181 | recluster_state(); 182 | } 183 | if (IsKeyPressed(KEY_SPACE)) { 184 | update_means(min_x, max_x, min_y, max_y); 185 | recluster_state(); 186 | } 187 | 188 | if (IsMouseButtonDown(MOUSE_BUTTON_LEFT)) { 189 | position = Vector2Subtract(position, Vector2Scale(GetMouseDelta(), 1/zoom)); 190 | } 191 | 192 | zoom += zoom_vel*dt; 193 | if (zoom < 1.0) zoom = 1.0; 194 | zoom_vel += GetMouseWheelMove()*50; 195 | zoom_vel *= 0.9; 196 | 197 | BeginDrawing(); 198 | ClearBackground(GetColor(0x181818AA)); 199 | Camera2D camera = { 200 | .zoom = zoom, 201 | .offset = (Vector2){w/2, h/2}, 202 | .target = position, 203 | }; 204 | BeginMode2D(camera); 205 | for (size_t i = 0; i < set.count; ++i) { 206 | Vector2 it = set.items[i]; 207 | DrawCircleV(it, SAMPLE_RADIUS/camera.zoom, RED); 208 | } 209 | for (size_t i = 0; i < K; ++i) { 210 | Color color = colors[i%colors_count]; 211 | 212 | for (size_t j = 0; j < clusters[i].count; ++j) { 213 | Vector2 it = clusters[i].items[j]; 214 | DrawCircleV(it, SAMPLE_RADIUS/camera.zoom, color); 215 | } 216 | 217 | DrawCircleV(means[i], MEAN_RADIUS/camera.zoom, WHITE); 218 | } 219 | EndMode2D(); 220 | EndDrawing(); 221 | } 222 | CloseWindow(); 223 | return 0; 224 | } 225 | -------------------------------------------------------------------------------- /src/3d.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "raylib.h" 7 | #include "raymath.h" 8 | 9 | #define IMAGE 10 | 11 | #define NOB_IMPLEMENTATION 12 | #include "nob.h" 13 | 14 | #define STB_DS_IMPLEMENTATION 15 | #include "stb_ds.h" 16 | 17 | #define K 4 18 | #define SAMPLE_SIZE 0.25 19 | #define SAMPLE_COLOR RED 20 | #define MEAN_SIZE (2*SAMPLE_SIZE) 21 | #define MEAN_COLOR WHITE 22 | #define CAMERA_SPEED SAMPLE_SIZE 23 | 24 | #include "common.c" 25 | 26 | typedef struct { 27 | Vector3 *items; 28 | size_t count; 29 | size_t capacity; 30 | } Samples3D; 31 | 32 | static void generate_cluster(Vector3 center, float radius, size_t count, Samples3D *samples) 33 | { 34 | for (size_t i = 0; i < count; ++i) { 35 | float mag = rand_float()*radius; 36 | float theta = rand_float()*2*PI; 37 | float phi = rand_float()*2*PI; 38 | Vector3 sample = { 39 | .x = sinf(theta)*cosf(phi)*mag, 40 | .y = sinf(theta)*sinf(phi)*mag, 41 | .z = cosf(theta)*mag, 42 | }; 43 | nob_da_append(samples, Vector3Add(sample, center)); 44 | } 45 | } 46 | 47 | static Samples3D set = {0}; 48 | static Samples3D clusters[K] = {0}; 49 | static Vector3 means[K] = {0}; 50 | 51 | void generate_new_state(float cluster_radius, size_t cluster_count) 52 | { 53 | #ifndef IMAGE 54 | generate_cluster((Vector3) {0, 0, 0}, cluster_radius, cluster_count, &set); 55 | generate_cluster((Vector3) {-cluster_radius, cluster_radius, 0}, cluster_radius/2, cluster_count/2, &set); 56 | generate_cluster((Vector3) {cluster_radius, cluster_radius, 0}, cluster_radius/2, cluster_count/2, &set); 57 | #else 58 | (void) cluster_count; 59 | #endif 60 | 61 | for (size_t i = 0; i < K; ++i) { 62 | means[i].x = Lerp(-cluster_radius, cluster_radius, rand_float()); 63 | means[i].y = Lerp(-cluster_radius, cluster_radius, rand_float()); 64 | means[i].z = Lerp(-cluster_radius, cluster_radius, rand_float()); 65 | } 66 | } 67 | 68 | void recluster_state(void) 69 | { 70 | for (size_t j = 0; j < K; ++j) { 71 | clusters[j].count = 0; 72 | } 73 | for (size_t i = 0; i < set.count; ++i) { 74 | Vector3 p = set.items[i]; 75 | int k = -1; 76 | float s = FLT_MAX; 77 | for (size_t j = 0; j < K; ++j) { 78 | Vector3 m = means[j]; 79 | float sm = Vector3LengthSqr(Vector3Subtract(p, m)); 80 | if (sm < s) { 81 | s = sm; 82 | k = j; 83 | } 84 | } 85 | nob_da_append(&clusters[k], p); 86 | } 87 | } 88 | 89 | void update_means(float cluster_radius) 90 | { 91 | for (size_t i = 0; i < K; ++i) { 92 | if (clusters[i].count > 0) { 93 | means[i] = Vector3Zero(); 94 | for (size_t j = 0; j < clusters[i].count; ++j) { 95 | means[i] = Vector3Add(means[i], clusters[i].items[j]); 96 | } 97 | means[i].x /= clusters[i].count; 98 | means[i].y /= clusters[i].count; 99 | means[i].z /= clusters[i].count; 100 | } else { 101 | means[i].x = Lerp(-cluster_radius, cluster_radius, rand_float()); 102 | means[i].y = Lerp(-cluster_radius, cluster_radius, rand_float()); 103 | means[i].z = Lerp(-cluster_radius, cluster_radius, rand_float()); 104 | } 105 | } 106 | } 107 | 108 | typedef struct { 109 | Color key; 110 | } Color_Point; 111 | 112 | int cluster_of_color(Color color, float cluster_radius) 113 | { 114 | Vector3 p = { 115 | .x = color.r/255.0f*cluster_radius, 116 | .y = color.g/255.0f*cluster_radius, 117 | .z = color.b/255.0f*cluster_radius, 118 | }; 119 | 120 | int k = -1; 121 | float s = FLT_MAX; 122 | for (size_t j = 0; j < K; ++j) { 123 | Vector3 m = means[j]; 124 | float sm = Vector3LengthSqr(Vector3Subtract(p, m)); 125 | if (sm < s) { 126 | s = sm; 127 | k = j; 128 | } 129 | } 130 | 131 | return k; 132 | } 133 | 134 | int main(int argc, char **argv) 135 | { 136 | float cluster_radius = 20; 137 | size_t cluster_count = 200; 138 | 139 | #ifdef IMAGE 140 | (void) generate_cluster; 141 | nob_shift_args(&argc, &argv); 142 | 143 | if (argc <= 0) { 144 | nob_log(NOB_ERROR, "No input image is provided"); 145 | return 1; 146 | } 147 | 148 | const char *file_path = nob_shift_args(&argc, &argv); 149 | Image image = LoadImage(file_path); 150 | ImageFormat(&image, PIXELFORMAT_UNCOMPRESSED_R8G8B8A8); 151 | 152 | Color_Point *unique_points = NULL; 153 | Color *points = image.data; 154 | size_t points_count = image.width*image.height; 155 | 156 | for (size_t i = 0; i < points_count; ++i) { 157 | ptrdiff_t index = hmgeti(unique_points, points[i]); 158 | if (index < 0) { 159 | Color_Point item = { points[i] }; 160 | hmputs(unique_points, item); 161 | } 162 | } 163 | 164 | size_t unique_points_count = hmlen(unique_points); 165 | for (size_t i = 0; i < unique_points_count; ++i) { 166 | Vector3 sample = { 167 | .x = unique_points[i].key.r/255.0f*cluster_radius, 168 | .y = unique_points[i].key.g/255.0f*cluster_radius, 169 | .z = unique_points[i].key.b/255.0f*cluster_radius, 170 | }; 171 | nob_da_append(&set, sample); 172 | } 173 | #endif // IMAGE 174 | 175 | generate_new_state(cluster_radius, cluster_count); 176 | recluster_state(); 177 | 178 | float camera_mag = 50; 179 | float camera_mag_vel = 0.0f; 180 | float camera_theta = 0.0; 181 | float camera_phi = 0.0; 182 | 183 | SetConfigFlags(FLAG_WINDOW_RESIZABLE); 184 | InitWindow(800, 600, "3D K-means"); 185 | if (!IsWindowReady()) return 1; 186 | while (!WindowShouldClose()) { 187 | if (IsKeyPressed(KEY_SPACE)) { 188 | update_means(cluster_radius); 189 | recluster_state(); 190 | } 191 | 192 | if (IsKeyPressed(KEY_S)) { 193 | for (size_t i = 0; i < points_count; ++i) { 194 | int k = cluster_of_color(points[i], cluster_radius); 195 | if (k < 0) nob_log(NOB_ERROR, "Color out of cluster"); 196 | Color color = { 197 | .r = means[k].x/cluster_radius*255, 198 | .g = means[k].y/cluster_radius*255, 199 | .b = means[k].z/cluster_radius*255, 200 | .a = 255, 201 | }; 202 | points[i] = color; 203 | } 204 | ExportImage(image, "output.png"); 205 | } 206 | 207 | float dt = GetFrameTime(); 208 | 209 | camera_mag += camera_mag_vel*dt; 210 | if (camera_mag < 0.0f) camera_mag = 0.0f; 211 | camera_mag_vel -= GetMouseWheelMove()*20; 212 | camera_mag_vel *= 0.9; 213 | 214 | if (IsMouseButtonDown(MOUSE_BUTTON_LEFT)) { 215 | Vector2 delta = GetMouseDelta(); 216 | camera_theta -= delta.x*0.01; 217 | // TODO: allow vertical rotation 218 | camera_phi -= delta.y*0.01; 219 | } 220 | 221 | BeginDrawing(); 222 | ClearBackground(GetColor(0x181818AA)); 223 | Camera3D camera = { 224 | .position = { 225 | .x = sinf(camera_theta)*cosf(camera_phi)*camera_mag, 226 | .y = sinf(camera_theta)*sinf(camera_phi)*camera_mag, 227 | .z = cosf(camera_theta)*camera_mag, 228 | }, 229 | .target = {0, 0, 0}, 230 | .up = {0, 1, 0}, 231 | .fovy = 90, 232 | .projection = CAMERA_PERSPECTIVE, 233 | }; 234 | BeginMode3D(camera); 235 | for (size_t i = 0; i < K; ++i) { 236 | Color color = { 237 | .r = means[i].x/cluster_radius*255, 238 | .g = means[i].y/cluster_radius*255, 239 | .b = means[i].z/cluster_radius*255, 240 | .a = 255, 241 | }; 242 | 243 | for (size_t j = 0; j < clusters[i].count; ++j) { 244 | Vector3 it = clusters[i].items[j]; 245 | DrawCube(it, SAMPLE_SIZE, SAMPLE_SIZE, SAMPLE_SIZE, color); 246 | } 247 | DrawCube(means[i], MEAN_SIZE, MEAN_SIZE, MEAN_SIZE, MEAN_COLOR); 248 | } 249 | EndMode3D(); 250 | EndDrawing(); 251 | } 252 | CloseWindow(); 253 | return 0; 254 | } 255 | -------------------------------------------------------------------------------- /src/arena.h: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Alexey Kutepov 2 | 3 | // Permission is hereby granted, free of charge, to any person obtaining 4 | // a copy of this software and associated documentation files (the 5 | // "Software"), to deal in the Software without restriction, including 6 | // without limitation the rights to use, copy, modify, merge, publish, 7 | // distribute, sublicense, and/or sell copies of the Software, and to 8 | // permit persons to whom the Software is furnished to do so, subject to 9 | // the following conditions: 10 | 11 | // The above copyright notice and this permission notice shall be 12 | // included in all copies or substantial portions of the Software. 13 | 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | 22 | #ifndef ARENA_H_ 23 | #define ARENA_H_ 24 | 25 | #include 26 | #include 27 | 28 | #ifndef ARENA_ASSERT 29 | #include 30 | #define ARENA_ASSERT assert 31 | #endif 32 | 33 | #define ARENA_BACKEND_LIBC_MALLOC 0 34 | #define ARENA_BACKEND_LINUX_MMAP 1 35 | #define ARENA_BACKEND_WIN32_VIRTUALALLOC 2 36 | #define ARENA_BACKEND_WASM_HEAPBASE 3 37 | 38 | #ifndef ARENA_BACKEND 39 | #define ARENA_BACKEND ARENA_BACKEND_LIBC_MALLOC 40 | #endif // ARENA_BACKEND 41 | 42 | typedef struct Region Region; 43 | 44 | struct Region { 45 | Region *next; 46 | size_t count; 47 | size_t capacity; 48 | uintptr_t data[]; 49 | }; 50 | 51 | typedef struct { 52 | Region *begin, *end; 53 | } Arena; 54 | 55 | #define REGION_DEFAULT_CAPACITY (8*1024) 56 | 57 | Region *new_region(size_t capacity); 58 | void free_region(Region *r); 59 | 60 | // TODO: snapshot/rewind capability for the arena 61 | // - Snapshot should be combination of a->end and a->end->count. 62 | // - Rewinding should be restoring a->end and a->end->count from the snapshot and 63 | // setting count-s of all the Region-s after the remembered a->end to 0. 64 | void *arena_alloc(Arena *a, size_t size_bytes); 65 | void *arena_realloc(Arena *a, void *oldptr, size_t oldsz, size_t newsz); 66 | char *arena_sprintf(Arena *a, const char *format, ...); 67 | 68 | void arena_reset(Arena *a); 69 | void arena_free(Arena *a); 70 | 71 | #endif // ARENA_H_ 72 | 73 | #ifdef ARENA_IMPLEMENTATION 74 | 75 | #if ARENA_BACKEND == ARENA_BACKEND_LIBC_MALLOC 76 | #include 77 | 78 | // TODO: instead of accepting specific capacity new_region() should accept the size of the object we want to fit into the region 79 | // It should be up to new_region() to decide the actual capacity to allocate 80 | Region *new_region(size_t capacity) 81 | { 82 | size_t size_bytes = sizeof(Region) + sizeof(uintptr_t)*capacity; 83 | // TODO: it would be nice if we could guarantee that the regions are allocated by ARENA_BACKEND_LIBC_MALLOC are page aligned 84 | Region *r = malloc(size_bytes); 85 | ARENA_ASSERT(r); 86 | r->next = NULL; 87 | r->count = 0; 88 | r->capacity = capacity; 89 | return r; 90 | } 91 | 92 | void free_region(Region *r) 93 | { 94 | free(r); 95 | } 96 | #elif ARENA_BACKEND == ARENA_BACKEND_LINUX_MMAP 97 | # error "TODO: Linux mmap backend is not implemented yet" 98 | #elif ARENA_BACKEND == ARENA_BACKEND_WIN32_VIRTUALALLOC 99 | 100 | #if !defined(_WIN32) 101 | # error "Current platform is not Windows" 102 | #endif 103 | 104 | #define WIN32_LEAN_AND_MEAN 105 | #include 106 | 107 | #define INV_HANDLE(x) (((x) == NULL) || ((x) == INVALID_HANDLE_VALUE)) 108 | 109 | Region *new_region(size_t capacity) 110 | { 111 | SIZE_T size_bytes = sizeof(Region) + sizeof(uintptr_t) * capacity; 112 | Region *r = VirtualAllocEx( 113 | GetCurrentProcess(), /* Allocate in current process address space */ 114 | NULL, /* Unknown position */ 115 | size_bytes, /* Bytes to allocate */ 116 | MEM_COMMIT | MEM_RESERVE, /* Reserve and commit allocated page */ 117 | PAGE_READWRITE /* Permissions ( Read/Write )*/ 118 | ); 119 | if (INV_HANDLE(r)) 120 | ARENA_ASSERT(0 && "VirtualAllocEx() failed."); 121 | 122 | r->next = NULL; 123 | r->count = 0; 124 | r->capacity = capacity; 125 | return r; 126 | } 127 | 128 | void free_region(Region *r) 129 | { 130 | if (INV_HANDLE(r)) 131 | return; 132 | 133 | BOOL free_result = VirtualFreeEx( 134 | GetCurrentProcess(), /* Deallocate from current process address space */ 135 | (LPVOID)r, /* Address to deallocate */ 136 | 0, /* Bytes to deallocate ( Unknown, deallocate entire page ) */ 137 | MEM_RELEASE /* Release the page ( And implicitly decommit it ) */ 138 | ); 139 | 140 | if (FALSE == free_result) 141 | ARENA_ASSERT(0 && "VirtualFreeEx() failed."); 142 | } 143 | 144 | #elif ARENA_BACKEND == ARENA_BACKEND_WASM_HEAPBASE 145 | # error "TODO: WASM __heap_base backend is not implemented yet" 146 | #else 147 | # error "Unknown Arena backend" 148 | #endif 149 | 150 | // TODO: add debug statistic collection mode for arena 151 | // Should collect things like: 152 | // - How many times new_region was called 153 | // - How many times existing region was skipped 154 | // - How many times allocation exceeded REGION_DEFAULT_CAPACITY 155 | 156 | void *arena_alloc(Arena *a, size_t size_bytes) 157 | { 158 | size_t size = (size_bytes + sizeof(uintptr_t) - 1)/sizeof(uintptr_t); 159 | 160 | if (a->end == NULL) { 161 | ARENA_ASSERT(a->begin == NULL); 162 | size_t capacity = REGION_DEFAULT_CAPACITY; 163 | if (capacity < size) capacity = size; 164 | a->end = new_region(capacity); 165 | a->begin = a->end; 166 | } 167 | 168 | while (a->end->count + size > a->end->capacity && a->end->next != NULL) { 169 | a->end = a->end->next; 170 | } 171 | 172 | if (a->end->count + size > a->end->capacity) { 173 | ARENA_ASSERT(a->end->next == NULL); 174 | size_t capacity = REGION_DEFAULT_CAPACITY; 175 | if (capacity < size) capacity = size; 176 | a->end->next = new_region(capacity); 177 | a->end = a->end->next; 178 | } 179 | 180 | void *result = &a->end->data[a->end->count]; 181 | a->end->count += size; 182 | return result; 183 | } 184 | 185 | void *arena_realloc(Arena *a, void *oldptr, size_t oldsz, size_t newsz) 186 | { 187 | if (newsz <= oldsz) return oldptr; 188 | void *newptr = arena_alloc(a, newsz); 189 | char *newptr_char = newptr; 190 | char *oldptr_char = oldptr; 191 | for (size_t i = 0; i < oldsz; ++i) { 192 | newptr_char[i] = oldptr_char[i]; 193 | } 194 | return newptr; 195 | } 196 | 197 | char *arena_sprintf(Arena *a, const char *format, ...) 198 | { 199 | va_list args; 200 | va_start(args, format); 201 | int n = vsnprintf(NULL, 0, format, args); 202 | va_end(args); 203 | 204 | ARENA_ASSERT(n >= 0); 205 | char *result = arena_alloc(a, n + 1); 206 | ARENA_ASSERT(result != NULL && "Extend the size of the temporary allocator"); 207 | // TODO: use proper arenas for the temporary allocator; 208 | va_start(args, format); 209 | vsnprintf(result, n + 1, format, args); 210 | va_end(args); 211 | 212 | return result; 213 | } 214 | 215 | void arena_reset(Arena *a) 216 | { 217 | for (Region *r = a->begin; r != NULL; r = r->next) { 218 | r->count = 0; 219 | } 220 | 221 | a->end = a->begin; 222 | } 223 | 224 | void arena_free(Arena *a) 225 | { 226 | Region *r = a->begin; 227 | while (r) { 228 | Region *r0 = r; 229 | r = r->next; 230 | free_region(r0); 231 | } 232 | a->begin = NULL; 233 | a->end = NULL; 234 | } 235 | 236 | #endif // ARENA_IMPLEMENTATION 237 | -------------------------------------------------------------------------------- /src/common.c: -------------------------------------------------------------------------------- 1 | static inline float rand_float(void) 2 | { 3 | return (float)rand()/RAND_MAX; 4 | } 5 | 6 | static Color colors[] = { 7 | GOLD, 8 | PINK, 9 | MAROON, 10 | LIME, 11 | SKYBLUE, 12 | VIOLET, 13 | }; 14 | #define colors_count NOB_ARRAY_LEN(colors) 15 | -------------------------------------------------------------------------------- /src/knn.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | #include 6 | 7 | #define NOB_IMPLEMENTATION 8 | #include "nob.h" 9 | #define ARENA_IMPLEMENTATION 10 | #include "arena.h" 11 | 12 | #define K 2 13 | 14 | // Stolen from https://gist.github.com/arq5x/5315739 15 | Nob_String_View deflate_sv(Arena *arena, Nob_String_View sv) 16 | { 17 | size_t output_size = sv.count*2; 18 | void *output = arena_alloc(arena, output_size); 19 | 20 | z_stream defstream = {0}; 21 | defstream.avail_in = (uInt)sv.count; 22 | defstream.next_in = (Bytef *)sv.data; 23 | defstream.avail_out = (uInt)output_size; 24 | defstream.next_out = (Bytef *)output; 25 | 26 | deflateInit(&defstream, Z_BEST_COMPRESSION); 27 | int result = deflate(&defstream, Z_FINISH); 28 | assert(result == Z_STREAM_END && "Probably not enough output buffer was allocated"); 29 | deflateEnd(&defstream); 30 | 31 | return nob_sv_from_parts(output, defstream.total_out); 32 | } 33 | 34 | typedef struct { 35 | size_t klass; 36 | Nob_String_View text; 37 | } Sample; 38 | 39 | typedef struct { 40 | Sample *items; 41 | size_t count; 42 | size_t capacity; 43 | } Samples; 44 | 45 | Samples parse_samples(Nob_String_View content) 46 | { 47 | size_t lines_count = 0; 48 | Samples samples = {0}; 49 | for (; content.count > 0; ++lines_count) { 50 | Nob_String_View line = nob_sv_chop_by_delim(&content, '\n'); 51 | if (lines_count == 0) continue; // ignore the header 52 | 53 | Nob_String_View klass = nob_sv_chop_by_delim(&line, ','); 54 | size_t klass_index = *klass.data - '0' - 1; 55 | 56 | nob_da_append(&samples, ((Sample) { 57 | .klass = klass_index, 58 | .text = line, 59 | })); 60 | } 61 | return samples; 62 | } 63 | 64 | const char *klass_names[] = {"World", "Sports", "Business", "Sci/Tech"}; 65 | 66 | typedef struct { 67 | float distance; 68 | size_t klass; 69 | } NCD; 70 | 71 | typedef struct { 72 | NCD *items; 73 | size_t count; 74 | size_t capacity; 75 | } NCDs; 76 | 77 | float ncd(Arena *arena, Nob_String_View a, Nob_String_View b, float cb) 78 | { 79 | Nob_String_View ab = nob_sv_from_cstr(arena_sprintf(arena, SV_Fmt" "SV_Fmt, SV_Arg(a), SV_Arg(b))); 80 | float ca = deflate_sv(arena, a).count; 81 | float cab = deflate_sv(arena, ab).count; 82 | float mn = ca; if (mn > cb) mn = cb; 83 | float mx = ca; if (mx < cb) mx = cb; 84 | return (cab - mn)/mx; 85 | } 86 | 87 | int compare_ncds(const void *a, const void *b) 88 | { 89 | const NCD *na = a; 90 | const NCD *nb = b; 91 | if (na->distance < nb->distance) return -1; 92 | if (na->distance > nb->distance) return 1; 93 | return 0; 94 | } 95 | 96 | typedef struct { 97 | Sample *train; 98 | size_t train_count; 99 | Nob_String_View text; 100 | 101 | NCDs ncds; 102 | Arena arena; 103 | } Klassify_State; 104 | 105 | void *klassify_thread(void *params) 106 | { 107 | Klassify_State *state = params; 108 | 109 | float cb = deflate_sv(&state->arena, state->text).count; 110 | for (size_t i = 0; i < state->train_count; ++i) { 111 | float distance = ncd(&state->arena, state->train[i].text, state->text, cb); 112 | arena_reset(&state->arena); 113 | nob_da_append(&state->ncds, ((NCD) { 114 | .distance = distance, 115 | .klass = state->train[i].klass, 116 | })); 117 | } 118 | 119 | return NULL; 120 | } 121 | 122 | typedef struct { 123 | size_t nprocs; 124 | size_t chunk_size; 125 | size_t chunk_rem; 126 | 127 | Samples train_samples; 128 | 129 | pthread_t *threads; 130 | Klassify_State *states; 131 | 132 | NCDs ncds; 133 | } Klass_Predictor; 134 | 135 | void klass_predictor_init(Klass_Predictor *kp, Samples train_samples) 136 | { 137 | kp->nprocs = get_nprocs(); 138 | kp->chunk_size = train_samples.count/kp->nprocs; 139 | kp->chunk_rem = train_samples.count%kp->nprocs; 140 | kp->train_samples = train_samples; 141 | 142 | kp->threads = malloc(kp->nprocs*sizeof(pthread_t)); 143 | assert(kp->threads != NULL); 144 | memset(kp->threads, 0, kp->nprocs*sizeof(pthread_t)); 145 | kp->states = malloc(kp->nprocs*sizeof(Klassify_State)); 146 | assert(kp->states != NULL); 147 | memset(kp->states, 0, kp->nprocs*sizeof(Klassify_State)); 148 | } 149 | 150 | size_t klass_predictor_predict(Klass_Predictor *kp, Nob_String_View text, size_t k) 151 | { 152 | for (size_t i = 0; i < kp->nprocs; ++i) { 153 | kp->states[i].train = kp->train_samples.items + i*kp->chunk_size; 154 | kp->states[i].train_count = kp->chunk_size; 155 | if (i == kp->nprocs - 1) kp->states[i].train_count += kp->chunk_rem; 156 | kp->states[i].text = text; 157 | kp->states[i].ncds.count = 0; 158 | arena_reset(&kp->states[i].arena); 159 | if (pthread_create(&kp->threads[i], NULL, klassify_thread, &kp->states[i]) != 0) { 160 | nob_log(NOB_ERROR, "Could not create thread"); 161 | exit(1); 162 | } 163 | } 164 | 165 | kp->ncds.count = 0; 166 | for (size_t i = 0; i < kp->nprocs; ++i) { 167 | if (pthread_join(kp->threads[i], NULL) != 0) { 168 | nob_log(NOB_ERROR, "Could not join thread"); 169 | exit(1); 170 | } 171 | nob_da_append_many(&kp->ncds, kp->states[i].ncds.items, kp->states[i].ncds.count); 172 | } 173 | qsort(kp->ncds.items, kp->ncds.count, sizeof(*kp->ncds.items), compare_ncds); 174 | 175 | size_t klass_freq[NOB_ARRAY_LEN(klass_names)] = {0}; 176 | for (size_t i = 0; i < k && i < kp->ncds.count; ++i) { 177 | klass_freq[kp->ncds.items[i].klass] += 1; 178 | } 179 | 180 | size_t predicted_klass = 0; 181 | for (size_t i = 1; i < NOB_ARRAY_LEN(klass_names); ++i) { 182 | if (klass_freq[predicted_klass] < klass_freq[i]) { 183 | predicted_klass = i; 184 | } 185 | } 186 | 187 | return predicted_klass; 188 | } 189 | 190 | char buffer[512]; 191 | 192 | double clock_get_secs(void) 193 | { 194 | struct timespec ts = {0}; 195 | int ret = clock_gettime(CLOCK_MONOTONIC, &ts); 196 | assert(ret == 0); 197 | return (double)ts.tv_sec + ts.tv_nsec*1e-9; 198 | } 199 | 200 | void usage(const char *program) 201 | { 202 | nob_log(NOB_ERROR, "Usage: %s [test.csv]", program); 203 | } 204 | 205 | void interactive_mode(Klass_Predictor *kp) 206 | { 207 | nob_log(NOB_INFO, "Provide News Title:"); 208 | while (true) { 209 | fgets(buffer, sizeof(buffer), stdin); 210 | double begin = clock_get_secs(); 211 | size_t predicted_klass = klass_predictor_predict(kp, nob_sv_from_cstr(buffer), K); 212 | double end = clock_get_secs(); 213 | nob_log(NOB_INFO, "Topic: %s (%.3lfsecs)", klass_names[predicted_klass], end - begin); 214 | } 215 | } 216 | 217 | int main(int argc, char **argv) 218 | { 219 | const char *program = nob_shift_args(&argc, &argv); 220 | 221 | if (argc <= 0) { 222 | usage(program); 223 | nob_log(NOB_ERROR, "ERROR: no train file is provided"); 224 | return 1; 225 | } 226 | const char *train_path = nob_shift_args(&argc, &argv); 227 | Nob_String_Builder train_content = {0}; 228 | if (!nob_read_entire_file(train_path, &train_content)) return 1; 229 | Samples train_samples = parse_samples(nob_sv_from_parts(train_content.items, train_content.count)); 230 | 231 | Klass_Predictor kp = {0}; 232 | klass_predictor_init(&kp, train_samples); 233 | 234 | if (argc <= 0) { 235 | interactive_mode(&kp); 236 | } else { 237 | const char *test_path = nob_shift_args(&argc, &argv); 238 | Nob_String_Builder test_content = {0}; 239 | if (!nob_read_entire_file(test_path, &test_content)) return 1; 240 | Samples test_samples = parse_samples(nob_sv_from_parts(test_content.items, test_content.count)); 241 | 242 | size_t success = 0; 243 | for (size_t i = 0; i < test_samples.count; ++i) { 244 | Nob_String_View text = test_samples.items[i].text; 245 | size_t actual_klass = test_samples.items[i].klass; 246 | 247 | double begin = clock_get_secs(); 248 | size_t predicted_klass = klass_predictor_predict(&kp, text, K); 249 | double end = clock_get_secs(); 250 | if (predicted_klass == actual_klass) success += 1; 251 | nob_log(NOB_INFO, "Text: "SV_Fmt, SV_Arg(text)); 252 | nob_log(NOB_INFO, "Predicted Topic: %s", klass_names[predicted_klass]); 253 | nob_log(NOB_INFO, "Actual Topic: %s", klass_names[actual_klass]); 254 | nob_log(NOB_INFO, "Elapsed Time: %.3lfsecs", end - begin); 255 | nob_log(NOB_INFO, "Success: %zu/%zu (%f)", success, test_samples.count, (float)success/test_samples.count); 256 | nob_log(NOB_INFO, "Progress: %zu/%zu (%f)", i + 1, test_samples.count, (float)(i + 1)/test_samples.count); 257 | nob_log(NOB_INFO, "Success rate: %zu/%zu (%f)", success, i + 1, (float)success/(i + 1)); 258 | nob_log(NOB_INFO, ""); 259 | } 260 | } 261 | 262 | return 0; 263 | } 264 | -------------------------------------------------------------------------------- /src/nob.h: -------------------------------------------------------------------------------- 1 | // This is a complete backward incompatible rewrite of https://github.com/tsoding/nobuild 2 | // because I'm really unhappy with the direction it is going. It's gonna sit in this repo 3 | // until it's matured enough and then I'll probably extract it to its own repo. 4 | 5 | // Copyright 2023 Alexey Kutepov 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining 8 | // a copy of this software and associated documentation files (the 9 | // "Software"), to deal in the Software without restriction, including 10 | // without limitation the rights to use, copy, modify, merge, publish, 11 | // distribute, sublicense, and/or sell copies of the Software, and to 12 | // permit persons to whom the Software is furnished to do so, subject to 13 | // the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be 16 | // included in all copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 20 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 21 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 22 | // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 24 | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 25 | 26 | #ifndef NOB_H_ 27 | #define NOB_H_ 28 | 29 | #define NOB_ASSERT assert 30 | #define NOB_REALLOC realloc 31 | #define NOB_FREE free 32 | 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | 42 | #ifdef _WIN32 43 | # define WIN32_LEAN_AND_MEAN 44 | # define _WINUSER_ 45 | # define _WINGDI_ 46 | # define _IMM_ 47 | # define _WINCON_ 48 | # include 49 | # include 50 | # include 51 | #else 52 | # include 53 | # include 54 | # include 55 | # include 56 | # include 57 | #endif 58 | 59 | #ifdef _WIN32 60 | # define NOB_LINE_END "\r\n" 61 | #else 62 | # define NOB_LINE_END "\n" 63 | #endif 64 | 65 | #define NOB_ARRAY_LEN(array) (sizeof(array)/sizeof(array[0])) 66 | #define NOB_ARRAY_GET(array, index) \ 67 | (NOB_ASSERT(index >= 0), NOB_ASSERT(index < NOB_ARRAY_LEN(array)), array[index]) 68 | 69 | typedef enum { 70 | NOB_INFO, 71 | NOB_WARNING, 72 | NOB_ERROR, 73 | } Nob_Log_Level; 74 | 75 | void nob_log(Nob_Log_Level level, const char *fmt, ...); 76 | 77 | // It is an equivalent of shift command from bash. It basically pops a command line 78 | // argument from the beginning. 79 | char *nob_shift_args(int *argc, char ***argv); 80 | 81 | typedef struct { 82 | const char **items; 83 | size_t count; 84 | size_t capacity; 85 | } Nob_File_Paths; 86 | 87 | typedef enum { 88 | NOB_FILE_REGULAR = 0, 89 | NOB_FILE_DIRECTORY, 90 | NOB_FILE_SYMLINK, 91 | NOB_FILE_OTHER, 92 | } Nob_File_Type; 93 | 94 | bool nob_mkdir_if_not_exists(const char *path); 95 | bool nob_copy_file(const char *src_path, const char *dst_path); 96 | bool nob_copy_directory_recursively(const char *src_path, const char *dst_path); 97 | bool nob_read_entire_dir(const char *parent, Nob_File_Paths *children); 98 | bool nob_write_entire_file(const char *path, const void *data, size_t size); 99 | Nob_File_Type nob_get_file_type(const char *path); 100 | 101 | #define nob_return_defer(value) do { result = (value); goto defer; } while(0) 102 | 103 | // Initial capacity of a dynamic array 104 | #define NOB_DA_INIT_CAP 256 105 | 106 | // Append an item to a dynamic array 107 | #define nob_da_append(da, item) \ 108 | do { \ 109 | if ((da)->count >= (da)->capacity) { \ 110 | (da)->capacity = (da)->capacity == 0 ? NOB_DA_INIT_CAP : (da)->capacity*2; \ 111 | (da)->items = NOB_REALLOC((da)->items, (da)->capacity*sizeof(*(da)->items)); \ 112 | NOB_ASSERT((da)->items != NULL && "Buy more RAM lol"); \ 113 | } \ 114 | \ 115 | (da)->items[(da)->count++] = (item); \ 116 | } while (0) 117 | 118 | #define nob_da_free(da) NOB_FREE((da).items) 119 | 120 | // Append several items to a dynamic array 121 | #define nob_da_append_many(da, new_items, new_items_count) \ 122 | do { \ 123 | if ((da)->count + new_items_count > (da)->capacity) { \ 124 | if ((da)->capacity == 0) { \ 125 | (da)->capacity = NOB_DA_INIT_CAP; \ 126 | } \ 127 | while ((da)->count + new_items_count > (da)->capacity) { \ 128 | (da)->capacity *= 2; \ 129 | } \ 130 | (da)->items = NOB_REALLOC((da)->items, (da)->capacity*sizeof(*(da)->items)); \ 131 | NOB_ASSERT((da)->items != NULL && "Buy more RAM lol"); \ 132 | } \ 133 | memcpy((da)->items + (da)->count, new_items, new_items_count*sizeof(*(da)->items)); \ 134 | (da)->count += new_items_count; \ 135 | } while (0) 136 | 137 | typedef struct { 138 | char *items; 139 | size_t count; 140 | size_t capacity; 141 | } Nob_String_Builder; 142 | 143 | bool nob_read_entire_file(const char *path, Nob_String_Builder *sb); 144 | 145 | // Append a sized buffer to a string builder 146 | #define nob_sb_append_buf(sb, buf, size) nob_da_append_many(sb, buf, size) 147 | 148 | // Append a NULL-terminated string to a string builder 149 | #define nob_sb_append_cstr(sb, cstr) \ 150 | do { \ 151 | const char *s = (cstr); \ 152 | size_t n = strlen(s); \ 153 | nob_da_append_many(sb, s, n); \ 154 | } while (0) 155 | 156 | // Append a single NULL character at the end of a string builder. So then you can 157 | // use it a NULL-terminated C string 158 | #define nob_sb_append_null(sb) nob_da_append_many(sb, "", 1) 159 | 160 | // Free the memory allocated by a string builder 161 | #define nob_sb_free(sb) NOB_FREE((sb).items) 162 | 163 | // Process handle 164 | #ifdef _WIN32 165 | typedef HANDLE Nob_Proc; 166 | #define NOB_INVALID_PROC INVALID_HANDLE_VALUE 167 | #else 168 | typedef int Nob_Proc; 169 | #define NOB_INVALID_PROC (-1) 170 | #endif // _WIN32 171 | 172 | typedef struct { 173 | Nob_Proc *items; 174 | size_t count; 175 | size_t capacity; 176 | } Nob_Procs; 177 | 178 | bool nob_procs_wait(Nob_Procs procs); 179 | 180 | // Wait until the process has finished 181 | bool nob_proc_wait(Nob_Proc proc); 182 | 183 | // A command - the main workhorse of Nob. Nob is all about building commands an running them 184 | typedef struct { 185 | const char **items; 186 | size_t count; 187 | size_t capacity; 188 | } Nob_Cmd; 189 | 190 | // Render a string representation of a command into a string builder. Keep in mind the the 191 | // string builder is not NULL-terminated by default. Use nob_sb_append_null if you plan to 192 | // use it as a C string. 193 | void nob_cmd_render(Nob_Cmd cmd, Nob_String_Builder *render); 194 | 195 | #define nob_cmd_append(cmd, ...) \ 196 | nob_da_append_many(cmd, ((const char*[]){__VA_ARGS__}), (sizeof((const char*[]){__VA_ARGS__})/sizeof(const char*))) 197 | 198 | // Free all the memory allocated by command arguments 199 | #define nob_cmd_free(cmd) NOB_FREE(cmd.items) 200 | 201 | // Run command asynchronously 202 | Nob_Proc nob_cmd_run_async(Nob_Cmd cmd); 203 | 204 | // Run command synchronously 205 | bool nob_cmd_run_sync(Nob_Cmd cmd); 206 | 207 | #ifndef NOB_TEMP_CAPACITY 208 | #define NOB_TEMP_CAPACITY (8*1024*1024) 209 | #endif // NOB_TEMP_CAPACITY 210 | char *nob_temp_strdup(const char *cstr); 211 | void *nob_temp_alloc(size_t size); 212 | char *nob_temp_sprintf(const char *format, ...); 213 | void nob_temp_reset(void); 214 | size_t nob_temp_save(void); 215 | void nob_temp_rewind(size_t checkpoint); 216 | 217 | int is_path1_modified_after_path2(const char *path1, const char *path2); 218 | bool nob_rename(const char *old_path, const char *new_path); 219 | int nob_needs_rebuild(const char *output_path, const char **input_paths, size_t input_paths_count); 220 | int nob_needs_rebuild1(const char *output_path, const char *input_path); 221 | int nob_file_exists(const char *file_path); 222 | 223 | // TODO: add MinGW support for Go Rebuild Urself™ Technology 224 | #ifndef NOB_REBUILD_URSELF 225 | # if _WIN32 226 | # if defined(__GNUC__) 227 | # define NOB_REBUILD_URSELF(binary_path, source_path) "gcc", "-o", binary_path, source_path 228 | # elif defined(__clang__) 229 | # define NOB_REBUILD_URSELF(binary_path, source_path) "clang", "-o", binary_path, source_path 230 | # elif defined(_MSC_VER) 231 | # define NOB_REBUILD_URSELF(binary_path, source_path) "cl.exe", source_path 232 | # endif 233 | # else 234 | # define NOB_REBUILD_URSELF(binary_path, source_path) "cc", "-o", binary_path, source_path 235 | # endif 236 | #endif 237 | 238 | // Go Rebuild Urself™ Technology 239 | // 240 | // How to use it: 241 | // int main(int argc, char** argv) { 242 | // GO_REBUILD_URSELF(argc, argv); 243 | // // actual work 244 | // return 0; 245 | // } 246 | // 247 | // After your added this macro every time you run ./nobuild it will detect 248 | // that you modified its original source code and will try to rebuild itself 249 | // before doing any actual work. So you only need to bootstrap your build system 250 | // once. 251 | // 252 | // The modification is detected by comparing the last modified times of the executable 253 | // and its source code. The same way the make utility usually does it. 254 | // 255 | // The rebuilding is done by using the REBUILD_URSELF macro which you can redefine 256 | // if you need a special way of bootstraping your build system. (which I personally 257 | // do not recommend since the whole idea of nobuild is to keep the process of bootstrapping 258 | // as simple as possible and doing all of the actual work inside of the nobuild) 259 | // 260 | #define NOB_GO_REBUILD_URSELF(argc, argv) \ 261 | do { \ 262 | const char *source_path = __FILE__; \ 263 | assert(argc >= 1); \ 264 | const char *binary_path = argv[0]; \ 265 | \ 266 | int rebuild_is_needed = nob_needs_rebuild(binary_path, &source_path, 1); \ 267 | if (rebuild_is_needed < 0) exit(1); \ 268 | if (rebuild_is_needed) { \ 269 | Nob_String_Builder sb = {0}; \ 270 | nob_sb_append_cstr(&sb, binary_path); \ 271 | nob_sb_append_cstr(&sb, ".old"); \ 272 | nob_sb_append_null(&sb); \ 273 | \ 274 | if (!nob_rename(binary_path, sb.items)) exit(1); \ 275 | Nob_Cmd rebuild = {0}; \ 276 | nob_cmd_append(&rebuild, NOB_REBUILD_URSELF(binary_path, source_path)); \ 277 | bool rebuild_succeeded = nob_cmd_run_sync(rebuild); \ 278 | nob_cmd_free(rebuild); \ 279 | if (!rebuild_succeeded) { \ 280 | nob_rename(sb.items, binary_path); \ 281 | exit(1); \ 282 | } \ 283 | \ 284 | Nob_Cmd cmd = {0}; \ 285 | nob_da_append_many(&cmd, argv, argc); \ 286 | if (!nob_cmd_run_sync(cmd)) exit(1); \ 287 | exit(0); \ 288 | } \ 289 | } while(0) 290 | // The implementation idea is stolen from https://github.com/zhiayang/nabs 291 | 292 | typedef struct { 293 | size_t count; 294 | const char *data; 295 | } Nob_String_View; 296 | 297 | const char *nob_temp_sv_to_cstr(Nob_String_View sv); 298 | 299 | Nob_String_View nob_sv_chop_by_delim(Nob_String_View *sv, char delim); 300 | Nob_String_View nob_sv_trim(Nob_String_View sv); 301 | bool nob_sv_eq(Nob_String_View a, Nob_String_View b); 302 | Nob_String_View nob_sv_from_cstr(const char *cstr); 303 | Nob_String_View nob_sv_from_parts(const char *data, size_t count); 304 | 305 | // printf macros for String_View 306 | #ifndef SV_Fmt 307 | #define SV_Fmt "%.*s" 308 | #endif // SV_Fmt 309 | #ifndef SV_Arg 310 | #define SV_Arg(sv) (int) (sv).count, (sv).data 311 | #endif // SV_Arg 312 | // USAGE: 313 | // String_View name = ...; 314 | // printf("Name: "SV_Fmt"\n", SV_Arg(name)); 315 | 316 | 317 | // minirent.h HEADER BEGIN //////////////////////////////////////// 318 | // Copyright 2021 Alexey Kutepov 319 | // 320 | // Permission is hereby granted, free of charge, to any person obtaining 321 | // a copy of this software and associated documentation files (the 322 | // "Software"), to deal in the Software without restriction, including 323 | // without limitation the rights to use, copy, modify, merge, publish, 324 | // distribute, sublicense, and/or sell copies of the Software, and to 325 | // permit persons to whom the Software is furnished to do so, subject to 326 | // the following conditions: 327 | // 328 | // The above copyright notice and this permission notice shall be 329 | // included in all copies or substantial portions of the Software. 330 | // 331 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 332 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 333 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 334 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 335 | // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 336 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 337 | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 338 | // 339 | // ============================================================ 340 | // 341 | // minirent — 0.0.1 — A subset of dirent interface for Windows. 342 | // 343 | // https://github.com/tsoding/minirent 344 | // 345 | // ============================================================ 346 | // 347 | // ChangeLog (https://semver.org/ is implied) 348 | // 349 | // 0.0.2 Automatically include dirent.h on non-Windows 350 | // platforms 351 | // 0.0.1 First Official Release 352 | 353 | #ifndef _WIN32 354 | #include 355 | #else // _WIN32 356 | 357 | #define WIN32_LEAN_AND_MEAN 358 | #include "windows.h" 359 | 360 | struct dirent 361 | { 362 | char d_name[MAX_PATH+1]; 363 | }; 364 | 365 | typedef struct DIR DIR; 366 | 367 | DIR *opendir(const char *dirpath); 368 | struct dirent *readdir(DIR *dirp); 369 | int closedir(DIR *dirp); 370 | #endif // _WIN32 371 | // minirent.h HEADER END //////////////////////////////////////// 372 | 373 | #endif // NOB_H_ 374 | 375 | #ifdef NOB_IMPLEMENTATION 376 | 377 | static size_t nob_temp_size = 0; 378 | static char nob_temp[NOB_TEMP_CAPACITY] = {0}; 379 | 380 | bool nob_mkdir_if_not_exists(const char *path) 381 | { 382 | #ifdef _WIN32 383 | int result = mkdir(path); 384 | #else 385 | int result = mkdir(path, 0755); 386 | #endif 387 | if (result < 0) { 388 | if (errno == EEXIST) { 389 | nob_log(NOB_INFO, "directory `%s` already exists", path); 390 | return true; 391 | } 392 | nob_log(NOB_ERROR, "could not create directory `%s`: %s", path, strerror(errno)); 393 | return false; 394 | } 395 | 396 | nob_log(NOB_INFO, "created directory `%s`", path); 397 | return true; 398 | } 399 | 400 | bool nob_copy_file(const char *src_path, const char *dst_path) 401 | { 402 | nob_log(NOB_INFO, "copying %s -> %s", src_path, dst_path); 403 | #ifdef _WIN32 404 | if (!CopyFile(src_path, dst_path, FALSE)) { 405 | nob_log(NOB_ERROR, "Could not copy file: %lu", GetLastError()); 406 | return false; 407 | } 408 | return true; 409 | #else 410 | int src_fd = -1; 411 | int dst_fd = -1; 412 | size_t buf_size = 32*1024; 413 | char *buf = NOB_REALLOC(NULL, buf_size); 414 | NOB_ASSERT(buf != NULL && "Buy more RAM lol!!"); 415 | bool result = true; 416 | 417 | src_fd = open(src_path, O_RDONLY); 418 | if (src_fd < 0) { 419 | nob_log(NOB_ERROR, "Could not open file %s: %s", src_path, strerror(errno)); 420 | nob_return_defer(false); 421 | } 422 | 423 | struct stat src_stat; 424 | if (fstat(src_fd, &src_stat) < 0) { 425 | nob_log(NOB_ERROR, "Could not get mode of file %s: %s", src_path, strerror(errno)); 426 | nob_return_defer(false); 427 | } 428 | 429 | dst_fd = open(dst_path, O_CREAT | O_TRUNC | O_WRONLY, src_stat.st_mode); 430 | if (dst_fd < 0) { 431 | nob_log(NOB_ERROR, "Could not create file %s: %s", dst_path, strerror(errno)); 432 | nob_return_defer(false); 433 | } 434 | 435 | for (;;) { 436 | ssize_t n = read(src_fd, buf, buf_size); 437 | if (n == 0) break; 438 | if (n < 0) { 439 | nob_log(NOB_ERROR, "Could not read from file %s: %s", src_path, strerror(errno)); 440 | nob_return_defer(false); 441 | } 442 | char *buf2 = buf; 443 | while (n > 0) { 444 | ssize_t m = write(dst_fd, buf2, n); 445 | if (m < 0) { 446 | nob_log(NOB_ERROR, "Could not write to file %s: %s", dst_path, strerror(errno)); 447 | nob_return_defer(false); 448 | } 449 | n -= m; 450 | buf2 += m; 451 | } 452 | } 453 | 454 | defer: 455 | free(buf); 456 | close(src_fd); 457 | close(dst_fd); 458 | return result; 459 | #endif 460 | } 461 | 462 | void nob_cmd_render(Nob_Cmd cmd, Nob_String_Builder *render) 463 | { 464 | for (size_t i = 0; i < cmd.count; ++i) { 465 | const char *arg = cmd.items[i]; 466 | if (arg == NULL) break; 467 | if (i > 0) nob_sb_append_cstr(render, " "); 468 | if (!strchr(arg, ' ')) { 469 | nob_sb_append_cstr(render, arg); 470 | } else { 471 | nob_da_append(render, '\''); 472 | nob_sb_append_cstr(render, arg); 473 | nob_da_append(render, '\''); 474 | } 475 | } 476 | } 477 | 478 | Nob_Proc nob_cmd_run_async(Nob_Cmd cmd) 479 | { 480 | if (cmd.count < 1) { 481 | nob_log(NOB_ERROR, "Could not run empty command"); 482 | return NOB_INVALID_PROC; 483 | } 484 | 485 | Nob_String_Builder sb = {0}; 486 | nob_cmd_render(cmd, &sb); 487 | nob_sb_append_null(&sb); 488 | nob_log(NOB_INFO, "CMD: %s", sb.items); 489 | nob_sb_free(sb); 490 | memset(&sb, 0, sizeof(sb)); 491 | 492 | #ifdef _WIN32 493 | // https://docs.microsoft.com/en-us/windows/win32/procthread/creating-a-child-process-with-redirected-input-and-output 494 | 495 | STARTUPINFO siStartInfo; 496 | ZeroMemory(&siStartInfo, sizeof(siStartInfo)); 497 | siStartInfo.cb = sizeof(STARTUPINFO); 498 | // NOTE: theoretically setting NULL to std handles should not be a problem 499 | // https://docs.microsoft.com/en-us/windows/console/getstdhandle?redirectedfrom=MSDN#attachdetach-behavior 500 | // TODO: check for errors in GetStdHandle 501 | siStartInfo.hStdError = GetStdHandle(STD_ERROR_HANDLE); 502 | siStartInfo.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE); 503 | siStartInfo.hStdInput = GetStdHandle(STD_INPUT_HANDLE); 504 | siStartInfo.dwFlags |= STARTF_USESTDHANDLES; 505 | 506 | PROCESS_INFORMATION piProcInfo; 507 | ZeroMemory(&piProcInfo, sizeof(PROCESS_INFORMATION)); 508 | 509 | // TODO: use a more reliable rendering of the command instead of cmd_render 510 | // cmd_render is for logging primarily 511 | nob_cmd_render(cmd, &sb); 512 | nob_sb_append_null(&sb); 513 | BOOL bSuccess = CreateProcessA(NULL, sb.items, NULL, NULL, TRUE, 0, NULL, NULL, &siStartInfo, &piProcInfo); 514 | nob_sb_free(sb); 515 | 516 | if (!bSuccess) { 517 | nob_log(NOB_ERROR, "Could not create child process: %lu", GetLastError()); 518 | return NOB_INVALID_PROC; 519 | } 520 | 521 | CloseHandle(piProcInfo.hThread); 522 | 523 | return piProcInfo.hProcess; 524 | #else 525 | pid_t cpid = fork(); 526 | if (cpid < 0) { 527 | nob_log(NOB_ERROR, "Could not fork child process: %s", strerror(errno)); 528 | return NOB_INVALID_PROC; 529 | } 530 | 531 | if (cpid == 0) { 532 | // NOTE: This leaks a bit of memory in the child process. 533 | // But do we actually care? It's a one off leak anyway... 534 | Nob_Cmd cmd_null = {0}; 535 | nob_da_append_many(&cmd_null, cmd.items, cmd.count); 536 | nob_cmd_append(&cmd_null, NULL); 537 | 538 | if (execvp(cmd.items[0], (char * const*) cmd_null.items) < 0) { 539 | nob_log(NOB_ERROR, "Could not exec child process: %s", strerror(errno)); 540 | exit(1); 541 | } 542 | NOB_ASSERT(0 && "unreachable"); 543 | } 544 | 545 | return cpid; 546 | #endif 547 | } 548 | 549 | bool nob_procs_wait(Nob_Procs procs) 550 | { 551 | bool success = true; 552 | for (size_t i = 0; i < procs.count; ++i) { 553 | success = nob_proc_wait(procs.items[i]) && success; 554 | } 555 | return success; 556 | } 557 | 558 | bool nob_proc_wait(Nob_Proc proc) 559 | { 560 | if (proc == NOB_INVALID_PROC) return false; 561 | 562 | #ifdef _WIN32 563 | DWORD result = WaitForSingleObject( 564 | proc, // HANDLE hHandle, 565 | INFINITE // DWORD dwMilliseconds 566 | ); 567 | 568 | if (result == WAIT_FAILED) { 569 | nob_log(NOB_ERROR, "could not wait on child process: %lu", GetLastError()); 570 | return false; 571 | } 572 | 573 | DWORD exit_status; 574 | if (!GetExitCodeProcess(proc, &exit_status)) { 575 | nob_log(NOB_ERROR, "could not get process exit code: %lu", GetLastError()); 576 | return false; 577 | } 578 | 579 | if (exit_status != 0) { 580 | nob_log(NOB_ERROR, "command exited with exit code %lu", exit_status); 581 | return false; 582 | } 583 | 584 | CloseHandle(proc); 585 | 586 | return true; 587 | #else 588 | for (;;) { 589 | int wstatus = 0; 590 | if (waitpid(proc, &wstatus, 0) < 0) { 591 | nob_log(NOB_ERROR, "could not wait on command (pid %d): %s", proc, strerror(errno)); 592 | return false; 593 | } 594 | 595 | if (WIFEXITED(wstatus)) { 596 | int exit_status = WEXITSTATUS(wstatus); 597 | if (exit_status != 0) { 598 | nob_log(NOB_ERROR, "command exited with exit code %d", exit_status); 599 | return false; 600 | } 601 | 602 | break; 603 | } 604 | 605 | if (WIFSIGNALED(wstatus)) { 606 | nob_log(NOB_ERROR, "command process was terminated by %s", strsignal(WTERMSIG(wstatus))); 607 | return false; 608 | } 609 | } 610 | 611 | return true; 612 | #endif 613 | } 614 | 615 | bool nob_cmd_run_sync(Nob_Cmd cmd) 616 | { 617 | Nob_Proc p = nob_cmd_run_async(cmd); 618 | if (p == NOB_INVALID_PROC) return false; 619 | return nob_proc_wait(p); 620 | } 621 | 622 | char *nob_shift_args(int *argc, char ***argv) 623 | { 624 | NOB_ASSERT(*argc > 0); 625 | char *result = **argv; 626 | (*argv) += 1; 627 | (*argc) -= 1; 628 | return result; 629 | } 630 | 631 | void nob_log(Nob_Log_Level level, const char *fmt, ...) 632 | { 633 | switch (level) { 634 | case NOB_INFO: 635 | fprintf(stderr, "[INFO] "); 636 | break; 637 | case NOB_WARNING: 638 | fprintf(stderr, "[WARNING] "); 639 | break; 640 | case NOB_ERROR: 641 | fprintf(stderr, "[ERROR] "); 642 | break; 643 | default: 644 | NOB_ASSERT(0 && "unreachable"); 645 | } 646 | 647 | va_list args; 648 | va_start(args, fmt); 649 | vfprintf(stderr, fmt, args); 650 | va_end(args); 651 | fprintf(stderr, "\n"); 652 | } 653 | 654 | bool nob_read_entire_dir(const char *parent, Nob_File_Paths *children) 655 | { 656 | bool result = true; 657 | DIR *dir = NULL; 658 | 659 | dir = opendir(parent); 660 | if (dir == NULL) { 661 | nob_log(NOB_ERROR, "Could not open directory %s: %s", parent, strerror(errno)); 662 | nob_return_defer(false); 663 | } 664 | 665 | errno = 0; 666 | struct dirent *ent = readdir(dir); 667 | while (ent != NULL) { 668 | nob_da_append(children, nob_temp_strdup(ent->d_name)); 669 | ent = readdir(dir); 670 | } 671 | 672 | if (errno != 0) { 673 | nob_log(NOB_ERROR, "Could not read directory %s: %s", parent, strerror(errno)); 674 | nob_return_defer(false); 675 | } 676 | 677 | defer: 678 | if (dir) closedir(dir); 679 | return result; 680 | } 681 | 682 | bool nob_write_entire_file(const char *path, const void *data, size_t size) 683 | { 684 | bool result = true; 685 | 686 | FILE *f = fopen(path, "wb"); 687 | if (f == NULL) { 688 | nob_log(NOB_ERROR, "Could not open file %s for writing: %s\n", path, strerror(errno)); 689 | nob_return_defer(false); 690 | } 691 | 692 | // len 693 | // v 694 | // aaaaaaaaaa 695 | // ^ 696 | // data 697 | 698 | const char *buf = data; 699 | while (size > 0) { 700 | size_t n = fwrite(buf, 1, size, f); 701 | if (ferror(f)) { 702 | nob_log(NOB_ERROR, "Could not write into file %s: %s\n", path, strerror(errno)); 703 | nob_return_defer(false); 704 | } 705 | size -= n; 706 | buf += n; 707 | } 708 | 709 | defer: 710 | if (f) fclose(f); 711 | return result; 712 | } 713 | 714 | Nob_File_Type nob_get_file_type(const char *path) 715 | { 716 | #ifdef _WIN32 717 | DWORD attr = GetFileAttributesA(path); 718 | if (attr == INVALID_FILE_ATTRIBUTES) { 719 | nob_log(NOB_ERROR, "Could not get file attributes of %s: %lu", path, GetLastError()); 720 | return -1; 721 | } 722 | 723 | if (attr & FILE_ATTRIBUTE_DIRECTORY) return NOB_FILE_DIRECTORY; 724 | // TODO: detect symlinks on Windows (whatever that means on Windows anyway) 725 | return NOB_FILE_REGULAR; 726 | #else // _WIN32 727 | struct stat statbuf; 728 | if (stat(path, &statbuf) < 0) { 729 | nob_log(NOB_ERROR, "Could not get stat of %s: %s", path, strerror(errno)); 730 | return -1; 731 | } 732 | 733 | switch (statbuf.st_mode & S_IFMT) { 734 | case S_IFDIR: return NOB_FILE_DIRECTORY; 735 | case S_IFREG: return NOB_FILE_REGULAR; 736 | case S_IFLNK: return NOB_FILE_SYMLINK; 737 | default: return NOB_FILE_OTHER; 738 | } 739 | #endif // _WIN32 740 | } 741 | 742 | bool nob_copy_directory_recursively(const char *src_path, const char *dst_path) 743 | { 744 | bool result = true; 745 | Nob_File_Paths children = {0}; 746 | Nob_String_Builder src_sb = {0}; 747 | Nob_String_Builder dst_sb = {0}; 748 | size_t temp_checkpoint = nob_temp_save(); 749 | 750 | Nob_File_Type type = nob_get_file_type(src_path); 751 | if (type < 0) return false; 752 | 753 | switch (type) { 754 | case NOB_FILE_DIRECTORY: { 755 | if (!nob_mkdir_if_not_exists(dst_path)) nob_return_defer(false); 756 | if (!nob_read_entire_dir(src_path, &children)) nob_return_defer(false); 757 | 758 | for (size_t i = 0; i < children.count; ++i) { 759 | if (strcmp(children.items[i], ".") == 0) continue; 760 | if (strcmp(children.items[i], "..") == 0) continue; 761 | 762 | src_sb.count = 0; 763 | nob_sb_append_cstr(&src_sb, src_path); 764 | nob_sb_append_cstr(&src_sb, "/"); 765 | nob_sb_append_cstr(&src_sb, children.items[i]); 766 | nob_sb_append_null(&src_sb); 767 | 768 | dst_sb.count = 0; 769 | nob_sb_append_cstr(&dst_sb, dst_path); 770 | nob_sb_append_cstr(&dst_sb, "/"); 771 | nob_sb_append_cstr(&dst_sb, children.items[i]); 772 | nob_sb_append_null(&dst_sb); 773 | 774 | if (!nob_copy_directory_recursively(src_sb.items, dst_sb.items)) { 775 | nob_return_defer(false); 776 | } 777 | } 778 | } break; 779 | 780 | case NOB_FILE_REGULAR: { 781 | if (!nob_copy_file(src_path, dst_path)) { 782 | nob_return_defer(false); 783 | } 784 | } break; 785 | 786 | case NOB_FILE_SYMLINK: { 787 | nob_log(NOB_WARNING, "TODO: Copying symlinks is not supported yet"); 788 | } break; 789 | 790 | case NOB_FILE_OTHER: { 791 | nob_log(NOB_ERROR, "Unsupported type of file %s", src_path); 792 | nob_return_defer(false); 793 | } break; 794 | 795 | default: NOB_ASSERT(0 && "unreachable"); 796 | } 797 | 798 | defer: 799 | nob_temp_rewind(temp_checkpoint); 800 | nob_da_free(src_sb); 801 | nob_da_free(dst_sb); 802 | nob_da_free(children); 803 | return result; 804 | } 805 | 806 | char *nob_temp_strdup(const char *cstr) 807 | { 808 | size_t n = strlen(cstr); 809 | char *result = nob_temp_alloc(n + 1); 810 | NOB_ASSERT(result != NULL && "Increase NOB_TEMP_CAPACITY"); 811 | memcpy(result, cstr, n); 812 | result[n] = '\0'; 813 | return result; 814 | } 815 | 816 | void *nob_temp_alloc(size_t size) 817 | { 818 | if (nob_temp_size + size > NOB_TEMP_CAPACITY) return NULL; 819 | void *result = &nob_temp[nob_temp_size]; 820 | nob_temp_size += size; 821 | return result; 822 | } 823 | 824 | char *nob_temp_sprintf(const char *format, ...) 825 | { 826 | va_list args; 827 | va_start(args, format); 828 | int n = vsnprintf(NULL, 0, format, args); 829 | va_end(args); 830 | 831 | NOB_ASSERT(n >= 0); 832 | char *result = nob_temp_alloc(n + 1); 833 | NOB_ASSERT(result != NULL && "Extend the size of the temporary allocator"); 834 | // TODO: use proper arenas for the temporary allocator; 835 | va_start(args, format); 836 | vsnprintf(result, n + 1, format, args); 837 | va_end(args); 838 | 839 | return result; 840 | } 841 | 842 | void nob_temp_reset(void) 843 | { 844 | nob_temp_size = 0; 845 | } 846 | 847 | size_t nob_temp_save(void) 848 | { 849 | return nob_temp_size; 850 | } 851 | 852 | void nob_temp_rewind(size_t checkpoint) 853 | { 854 | nob_temp_size = checkpoint; 855 | } 856 | 857 | const char *nob_temp_sv_to_cstr(Nob_String_View sv) 858 | { 859 | char *result = nob_temp_alloc(sv.count + 1); 860 | NOB_ASSERT(result != NULL && "Extend the size of the temporary allocator"); 861 | memcpy(result, sv.data, sv.count); 862 | result[sv.count] = '\0'; 863 | return result; 864 | } 865 | 866 | int nob_needs_rebuild(const char *output_path, const char **input_paths, size_t input_paths_count) 867 | { 868 | #ifdef _WIN32 869 | BOOL bSuccess; 870 | 871 | HANDLE output_path_fd = CreateFile(output_path, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_READONLY, NULL); 872 | if (output_path_fd == INVALID_HANDLE_VALUE) { 873 | // NOTE: if output does not exist it 100% must be rebuilt 874 | if (GetLastError() == ERROR_FILE_NOT_FOUND) return 1; 875 | nob_log(NOB_ERROR, "Could not open file %s: %lu", output_path, GetLastError()); 876 | return -1; 877 | } 878 | FILETIME output_path_time; 879 | bSuccess = GetFileTime(output_path_fd, NULL, NULL, &output_path_time); 880 | CloseHandle(output_path_fd); 881 | if (!bSuccess) { 882 | nob_log(NOB_ERROR, "Could not get time of %s: %lu", output_path, GetLastError()); 883 | return -1; 884 | } 885 | 886 | for (size_t i = 0; i < input_paths_count; ++i) { 887 | const char *input_path = input_paths[i]; 888 | HANDLE input_path_fd = CreateFile(input_path, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_READONLY, NULL); 889 | if (input_path_fd == INVALID_HANDLE_VALUE) { 890 | // NOTE: non-existing input is an error cause it is needed for building in the first place 891 | nob_log(NOB_ERROR, "Could not open file %s: %lu", input_path, GetLastError()); 892 | return -1; 893 | } 894 | FILETIME input_path_time; 895 | bSuccess = GetFileTime(input_path_fd, NULL, NULL, &input_path_time); 896 | CloseHandle(input_path_fd); 897 | if (!bSuccess) { 898 | nob_log(NOB_ERROR, "Could not get time of %s: %lu", input_path, GetLastError()); 899 | return -1; 900 | } 901 | 902 | // NOTE: if even a single input_path is fresher than output_path that's 100% rebuild 903 | if (CompareFileTime(&input_path_time, &output_path_time) == 1) return 1; 904 | } 905 | 906 | return 0; 907 | #else 908 | struct stat statbuf = {0}; 909 | 910 | if (stat(output_path, &statbuf) < 0) { 911 | // NOTE: if output does not exist it 100% must be rebuilt 912 | if (errno == ENOENT) return 1; 913 | nob_log(NOB_ERROR, "could not stat %s: %s", output_path, strerror(errno)); 914 | return -1; 915 | } 916 | int output_path_time = statbuf.st_mtime; 917 | 918 | for (size_t i = 0; i < input_paths_count; ++i) { 919 | const char *input_path = input_paths[i]; 920 | if (stat(input_path, &statbuf) < 0) { 921 | // NOTE: non-existing input is an error cause it is needed for building in the first place 922 | nob_log(NOB_ERROR, "could not stat %s: %s", input_path, strerror(errno)); 923 | return -1; 924 | } 925 | int input_path_time = statbuf.st_mtime; 926 | // NOTE: if even a single input_path is fresher than output_path that's 100% rebuild 927 | if (input_path_time > output_path_time) return 1; 928 | } 929 | 930 | return 0; 931 | #endif 932 | } 933 | 934 | int nob_needs_rebuild1(const char *output_path, const char *input_path) 935 | { 936 | return nob_needs_rebuild(output_path, &input_path, 1); 937 | } 938 | 939 | bool nob_rename(const char *old_path, const char *new_path) 940 | { 941 | nob_log(NOB_INFO, "renaming %s -> %s", old_path, new_path); 942 | #ifdef _WIN32 943 | if (!MoveFileEx(old_path, new_path, MOVEFILE_REPLACE_EXISTING)) { 944 | nob_log(NOB_ERROR, "could not rename %s to %s: %lu", old_path, new_path, GetLastError()); 945 | return false; 946 | } 947 | #else 948 | if (rename(old_path, new_path) < 0) { 949 | nob_log(NOB_ERROR, "could not rename %s to %s: %s", old_path, new_path, strerror(errno)); 950 | return false; 951 | } 952 | #endif // _WIN32 953 | return true; 954 | } 955 | 956 | bool nob_read_entire_file(const char *path, Nob_String_Builder *sb) 957 | { 958 | bool result = true; 959 | 960 | FILE *f = fopen(path, "rb"); 961 | if (f == NULL) nob_return_defer(false); 962 | if (fseek(f, 0, SEEK_END) < 0) nob_return_defer(false); 963 | long m = ftell(f); 964 | if (m < 0) nob_return_defer(false); 965 | if (fseek(f, 0, SEEK_SET) < 0) nob_return_defer(false); 966 | 967 | size_t new_count = sb->count + m; 968 | if (new_count > sb->capacity) { 969 | sb->items = realloc(sb->items, new_count); 970 | NOB_ASSERT(sb->items != NULL && "Buy more RAM lool!!"); 971 | sb->capacity = new_count; 972 | } 973 | 974 | fread(sb->items + sb->count, m, 1, f); 975 | if (ferror(f)) { 976 | // TODO: Afaik, ferror does not set errno. So the error reporting in defer is not correct in this case. 977 | nob_return_defer(false); 978 | } 979 | sb->count = new_count; 980 | 981 | defer: 982 | if (!result) nob_log(NOB_ERROR, "Could not read file %s: %s", path, strerror(errno)); 983 | if (f) fclose(f); 984 | return result; 985 | } 986 | 987 | Nob_String_View nob_sv_chop_by_delim(Nob_String_View *sv, char delim) 988 | { 989 | size_t i = 0; 990 | while (i < sv->count && sv->data[i] != delim) { 991 | i += 1; 992 | } 993 | 994 | Nob_String_View result = nob_sv_from_parts(sv->data, i); 995 | 996 | if (i < sv->count) { 997 | sv->count -= i + 1; 998 | sv->data += i + 1; 999 | } else { 1000 | sv->count -= i; 1001 | sv->data += i; 1002 | } 1003 | 1004 | return result; 1005 | } 1006 | 1007 | Nob_String_View nob_sv_from_parts(const char *data, size_t count) 1008 | { 1009 | Nob_String_View sv; 1010 | sv.count = count; 1011 | sv.data = data; 1012 | return sv; 1013 | } 1014 | 1015 | Nob_String_View nob_sv_trim_left(Nob_String_View sv) 1016 | { 1017 | size_t i = 0; 1018 | while (i < sv.count && isspace(sv.data[i])) { 1019 | i += 1; 1020 | } 1021 | 1022 | return nob_sv_from_parts(sv.data + i, sv.count - i); 1023 | } 1024 | 1025 | Nob_String_View nob_sv_trim_right(Nob_String_View sv) 1026 | { 1027 | size_t i = 0; 1028 | while (i < sv.count && isspace(sv.data[sv.count - 1 - i])) { 1029 | i += 1; 1030 | } 1031 | 1032 | return nob_sv_from_parts(sv.data, sv.count - i); 1033 | } 1034 | 1035 | Nob_String_View nob_sv_trim(Nob_String_View sv) 1036 | { 1037 | return nob_sv_trim_right(nob_sv_trim_left(sv)); 1038 | } 1039 | 1040 | Nob_String_View nob_sv_from_cstr(const char *cstr) 1041 | { 1042 | return nob_sv_from_parts(cstr, strlen(cstr)); 1043 | } 1044 | 1045 | bool nob_sv_eq(Nob_String_View a, Nob_String_View b) 1046 | { 1047 | if (a.count != b.count) { 1048 | return false; 1049 | } else { 1050 | return memcmp(a.data, b.data, a.count) == 0; 1051 | } 1052 | } 1053 | 1054 | // RETURNS: 1055 | // 0 - file does not exists 1056 | // 1 - file exists 1057 | // -1 - error while checking if file exists. The error is logged 1058 | int nob_file_exists(const char *file_path) 1059 | { 1060 | #if _WIN32 1061 | // TODO: distinguish between "does not exists" and other errors 1062 | DWORD dwAttrib = GetFileAttributesA(file_path); 1063 | return dwAttrib != INVALID_FILE_ATTRIBUTES; 1064 | #else 1065 | struct stat statbuf; 1066 | if (stat(file_path, &statbuf) < 0) { 1067 | if (errno == ENOENT) return 0; 1068 | nob_log(NOB_ERROR, "Could not check if file %s exists: %s", file_path, strerror(errno)); 1069 | return -1; 1070 | } 1071 | return 1; 1072 | #endif 1073 | } 1074 | 1075 | // minirent.h SOURCE BEGIN //////////////////////////////////////// 1076 | #ifdef _WIN32 1077 | struct DIR 1078 | { 1079 | HANDLE hFind; 1080 | WIN32_FIND_DATA data; 1081 | struct dirent *dirent; 1082 | }; 1083 | 1084 | DIR *opendir(const char *dirpath) 1085 | { 1086 | assert(dirpath); 1087 | 1088 | char buffer[MAX_PATH]; 1089 | snprintf(buffer, MAX_PATH, "%s\\*", dirpath); 1090 | 1091 | DIR *dir = (DIR*)calloc(1, sizeof(DIR)); 1092 | 1093 | dir->hFind = FindFirstFile(buffer, &dir->data); 1094 | if (dir->hFind == INVALID_HANDLE_VALUE) { 1095 | // TODO: opendir should set errno accordingly on FindFirstFile fail 1096 | // https://docs.microsoft.com/en-us/windows/win32/api/errhandlingapi/nf-errhandlingapi-getlasterror 1097 | errno = ENOSYS; 1098 | goto fail; 1099 | } 1100 | 1101 | return dir; 1102 | 1103 | fail: 1104 | if (dir) { 1105 | free(dir); 1106 | } 1107 | 1108 | return NULL; 1109 | } 1110 | 1111 | struct dirent *readdir(DIR *dirp) 1112 | { 1113 | assert(dirp); 1114 | 1115 | if (dirp->dirent == NULL) { 1116 | dirp->dirent = (struct dirent*)calloc(1, sizeof(struct dirent)); 1117 | } else { 1118 | if(!FindNextFile(dirp->hFind, &dirp->data)) { 1119 | if (GetLastError() != ERROR_NO_MORE_FILES) { 1120 | // TODO: readdir should set errno accordingly on FindNextFile fail 1121 | // https://docs.microsoft.com/en-us/windows/win32/api/errhandlingapi/nf-errhandlingapi-getlasterror 1122 | errno = ENOSYS; 1123 | } 1124 | 1125 | return NULL; 1126 | } 1127 | } 1128 | 1129 | memset(dirp->dirent->d_name, 0, sizeof(dirp->dirent->d_name)); 1130 | 1131 | strncpy( 1132 | dirp->dirent->d_name, 1133 | dirp->data.cFileName, 1134 | sizeof(dirp->dirent->d_name) - 1); 1135 | 1136 | return dirp->dirent; 1137 | } 1138 | 1139 | int closedir(DIR *dirp) 1140 | { 1141 | assert(dirp); 1142 | 1143 | if(!FindClose(dirp->hFind)) { 1144 | // TODO: closedir should set errno accordingly on FindClose fail 1145 | // https://docs.microsoft.com/en-us/windows/win32/api/errhandlingapi/nf-errhandlingapi-getlasterror 1146 | errno = ENOSYS; 1147 | return -1; 1148 | } 1149 | 1150 | if (dirp->dirent) { 1151 | free(dirp->dirent); 1152 | } 1153 | free(dirp); 1154 | 1155 | return 0; 1156 | } 1157 | #endif // _WIN32 1158 | // minirent.h SOURCE END //////////////////////////////////////// 1159 | 1160 | #endif 1161 | -------------------------------------------------------------------------------- /zlib/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright notice: 2 | 3 | (C) 1995-2022 Jean-loup Gailly and Mark Adler 4 | 5 | This software is provided 'as-is', without any express or implied 6 | warranty. In no event will the authors be held liable for any damages 7 | arising from the use of this software. 8 | 9 | Permission is granted to anyone to use this software for any purpose, 10 | including commercial applications, and to alter it and redistribute it 11 | freely, subject to the following restrictions: 12 | 13 | 1. The origin of this software must not be misrepresented; you must not 14 | claim that you wrote the original software. If you use this software 15 | in a product, an acknowledgment in the product documentation would be 16 | appreciated but is not required. 17 | 2. Altered source versions must be plainly marked as such, and must not be 18 | misrepresented as being the original software. 19 | 3. This notice may not be removed or altered from any source distribution. 20 | 21 | Jean-loup Gailly Mark Adler 22 | jloup@gzip.org madler@alumni.caltech.edu 23 | -------------------------------------------------------------------------------- /zlib/libz.a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tsoding/data-mining-in-c/2cd2cc4ad484354465fab37f6543db18c9d660ca/zlib/libz.a -------------------------------------------------------------------------------- /zlib/zconf.h: -------------------------------------------------------------------------------- 1 | /* zconf.h -- configuration of the zlib compression library 2 | * Copyright (C) 1995-2016 Jean-loup Gailly, Mark Adler 3 | * For conditions of distribution and use, see copyright notice in zlib.h 4 | */ 5 | 6 | /* @(#) $Id$ */ 7 | 8 | #ifndef ZCONF_H 9 | #define ZCONF_H 10 | 11 | /* 12 | * If you *really* need a unique prefix for all types and library functions, 13 | * compile with -DZ_PREFIX. The "standard" zlib should be compiled without it. 14 | * Even better than compiling with -DZ_PREFIX would be to use configure to set 15 | * this permanently in zconf.h using "./configure --zprefix". 16 | */ 17 | #ifdef Z_PREFIX /* may be set to #if 1 by ./configure */ 18 | # define Z_PREFIX_SET 19 | 20 | /* all linked symbols and init macros */ 21 | # define _dist_code z__dist_code 22 | # define _length_code z__length_code 23 | # define _tr_align z__tr_align 24 | # define _tr_flush_bits z__tr_flush_bits 25 | # define _tr_flush_block z__tr_flush_block 26 | # define _tr_init z__tr_init 27 | # define _tr_stored_block z__tr_stored_block 28 | # define _tr_tally z__tr_tally 29 | # define adler32 z_adler32 30 | # define adler32_combine z_adler32_combine 31 | # define adler32_combine64 z_adler32_combine64 32 | # define adler32_z z_adler32_z 33 | # ifndef Z_SOLO 34 | # define compress z_compress 35 | # define compress2 z_compress2 36 | # define compressBound z_compressBound 37 | # endif 38 | # define crc32 z_crc32 39 | # define crc32_combine z_crc32_combine 40 | # define crc32_combine64 z_crc32_combine64 41 | # define crc32_combine_gen z_crc32_combine_gen 42 | # define crc32_combine_gen64 z_crc32_combine_gen64 43 | # define crc32_combine_op z_crc32_combine_op 44 | # define crc32_z z_crc32_z 45 | # define deflate z_deflate 46 | # define deflateBound z_deflateBound 47 | # define deflateCopy z_deflateCopy 48 | # define deflateEnd z_deflateEnd 49 | # define deflateGetDictionary z_deflateGetDictionary 50 | # define deflateInit z_deflateInit 51 | # define deflateInit2 z_deflateInit2 52 | # define deflateInit2_ z_deflateInit2_ 53 | # define deflateInit_ z_deflateInit_ 54 | # define deflateParams z_deflateParams 55 | # define deflatePending z_deflatePending 56 | # define deflatePrime z_deflatePrime 57 | # define deflateReset z_deflateReset 58 | # define deflateResetKeep z_deflateResetKeep 59 | # define deflateSetDictionary z_deflateSetDictionary 60 | # define deflateSetHeader z_deflateSetHeader 61 | # define deflateTune z_deflateTune 62 | # define deflate_copyright z_deflate_copyright 63 | # define get_crc_table z_get_crc_table 64 | # ifndef Z_SOLO 65 | # define gz_error z_gz_error 66 | # define gz_intmax z_gz_intmax 67 | # define gz_strwinerror z_gz_strwinerror 68 | # define gzbuffer z_gzbuffer 69 | # define gzclearerr z_gzclearerr 70 | # define gzclose z_gzclose 71 | # define gzclose_r z_gzclose_r 72 | # define gzclose_w z_gzclose_w 73 | # define gzdirect z_gzdirect 74 | # define gzdopen z_gzdopen 75 | # define gzeof z_gzeof 76 | # define gzerror z_gzerror 77 | # define gzflush z_gzflush 78 | # define gzfread z_gzfread 79 | # define gzfwrite z_gzfwrite 80 | # define gzgetc z_gzgetc 81 | # define gzgetc_ z_gzgetc_ 82 | # define gzgets z_gzgets 83 | # define gzoffset z_gzoffset 84 | # define gzoffset64 z_gzoffset64 85 | # define gzopen z_gzopen 86 | # define gzopen64 z_gzopen64 87 | # ifdef _WIN32 88 | # define gzopen_w z_gzopen_w 89 | # endif 90 | # define gzprintf z_gzprintf 91 | # define gzputc z_gzputc 92 | # define gzputs z_gzputs 93 | # define gzread z_gzread 94 | # define gzrewind z_gzrewind 95 | # define gzseek z_gzseek 96 | # define gzseek64 z_gzseek64 97 | # define gzsetparams z_gzsetparams 98 | # define gztell z_gztell 99 | # define gztell64 z_gztell64 100 | # define gzungetc z_gzungetc 101 | # define gzvprintf z_gzvprintf 102 | # define gzwrite z_gzwrite 103 | # endif 104 | # define inflate z_inflate 105 | # define inflateBack z_inflateBack 106 | # define inflateBackEnd z_inflateBackEnd 107 | # define inflateBackInit z_inflateBackInit 108 | # define inflateBackInit_ z_inflateBackInit_ 109 | # define inflateCodesUsed z_inflateCodesUsed 110 | # define inflateCopy z_inflateCopy 111 | # define inflateEnd z_inflateEnd 112 | # define inflateGetDictionary z_inflateGetDictionary 113 | # define inflateGetHeader z_inflateGetHeader 114 | # define inflateInit z_inflateInit 115 | # define inflateInit2 z_inflateInit2 116 | # define inflateInit2_ z_inflateInit2_ 117 | # define inflateInit_ z_inflateInit_ 118 | # define inflateMark z_inflateMark 119 | # define inflatePrime z_inflatePrime 120 | # define inflateReset z_inflateReset 121 | # define inflateReset2 z_inflateReset2 122 | # define inflateResetKeep z_inflateResetKeep 123 | # define inflateSetDictionary z_inflateSetDictionary 124 | # define inflateSync z_inflateSync 125 | # define inflateSyncPoint z_inflateSyncPoint 126 | # define inflateUndermine z_inflateUndermine 127 | # define inflateValidate z_inflateValidate 128 | # define inflate_copyright z_inflate_copyright 129 | # define inflate_fast z_inflate_fast 130 | # define inflate_table z_inflate_table 131 | # ifndef Z_SOLO 132 | # define uncompress z_uncompress 133 | # define uncompress2 z_uncompress2 134 | # endif 135 | # define zError z_zError 136 | # ifndef Z_SOLO 137 | # define zcalloc z_zcalloc 138 | # define zcfree z_zcfree 139 | # endif 140 | # define zlibCompileFlags z_zlibCompileFlags 141 | # define zlibVersion z_zlibVersion 142 | 143 | /* all zlib typedefs in zlib.h and zconf.h */ 144 | # define Byte z_Byte 145 | # define Bytef z_Bytef 146 | # define alloc_func z_alloc_func 147 | # define charf z_charf 148 | # define free_func z_free_func 149 | # ifndef Z_SOLO 150 | # define gzFile z_gzFile 151 | # endif 152 | # define gz_header z_gz_header 153 | # define gz_headerp z_gz_headerp 154 | # define in_func z_in_func 155 | # define intf z_intf 156 | # define out_func z_out_func 157 | # define uInt z_uInt 158 | # define uIntf z_uIntf 159 | # define uLong z_uLong 160 | # define uLongf z_uLongf 161 | # define voidp z_voidp 162 | # define voidpc z_voidpc 163 | # define voidpf z_voidpf 164 | 165 | /* all zlib structs in zlib.h and zconf.h */ 166 | # define gz_header_s z_gz_header_s 167 | # define internal_state z_internal_state 168 | 169 | #endif 170 | 171 | #if defined(__MSDOS__) && !defined(MSDOS) 172 | # define MSDOS 173 | #endif 174 | #if (defined(OS_2) || defined(__OS2__)) && !defined(OS2) 175 | # define OS2 176 | #endif 177 | #if defined(_WINDOWS) && !defined(WINDOWS) 178 | # define WINDOWS 179 | #endif 180 | #if defined(_WIN32) || defined(_WIN32_WCE) || defined(__WIN32__) 181 | # ifndef WIN32 182 | # define WIN32 183 | # endif 184 | #endif 185 | #if (defined(MSDOS) || defined(OS2) || defined(WINDOWS)) && !defined(WIN32) 186 | # if !defined(__GNUC__) && !defined(__FLAT__) && !defined(__386__) 187 | # ifndef SYS16BIT 188 | # define SYS16BIT 189 | # endif 190 | # endif 191 | #endif 192 | 193 | /* 194 | * Compile with -DMAXSEG_64K if the alloc function cannot allocate more 195 | * than 64k bytes at a time (needed on systems with 16-bit int). 196 | */ 197 | #ifdef SYS16BIT 198 | # define MAXSEG_64K 199 | #endif 200 | #ifdef MSDOS 201 | # define UNALIGNED_OK 202 | #endif 203 | 204 | #ifdef __STDC_VERSION__ 205 | # ifndef STDC 206 | # define STDC 207 | # endif 208 | # if __STDC_VERSION__ >= 199901L 209 | # ifndef STDC99 210 | # define STDC99 211 | # endif 212 | # endif 213 | #endif 214 | #if !defined(STDC) && (defined(__STDC__) || defined(__cplusplus)) 215 | # define STDC 216 | #endif 217 | #if !defined(STDC) && (defined(__GNUC__) || defined(__BORLANDC__)) 218 | # define STDC 219 | #endif 220 | #if !defined(STDC) && (defined(MSDOS) || defined(WINDOWS) || defined(WIN32)) 221 | # define STDC 222 | #endif 223 | #if !defined(STDC) && (defined(OS2) || defined(__HOS_AIX__)) 224 | # define STDC 225 | #endif 226 | 227 | #if defined(__OS400__) && !defined(STDC) /* iSeries (formerly AS/400). */ 228 | # define STDC 229 | #endif 230 | 231 | #ifndef STDC 232 | # ifndef const /* cannot use !defined(STDC) && !defined(const) on Mac */ 233 | # define const /* note: need a more gentle solution here */ 234 | # endif 235 | #endif 236 | 237 | #if defined(ZLIB_CONST) && !defined(z_const) 238 | # define z_const const 239 | #else 240 | # define z_const 241 | #endif 242 | 243 | #ifdef Z_SOLO 244 | # ifdef _WIN64 245 | typedef unsigned long long z_size_t; 246 | # else 247 | typedef unsigned long z_size_t; 248 | # endif 249 | #else 250 | # define z_longlong long long 251 | # if defined(NO_SIZE_T) 252 | typedef unsigned NO_SIZE_T z_size_t; 253 | # elif defined(STDC) 254 | # include 255 | typedef size_t z_size_t; 256 | # else 257 | typedef unsigned long z_size_t; 258 | # endif 259 | # undef z_longlong 260 | #endif 261 | 262 | /* Maximum value for memLevel in deflateInit2 */ 263 | #ifndef MAX_MEM_LEVEL 264 | # ifdef MAXSEG_64K 265 | # define MAX_MEM_LEVEL 8 266 | # else 267 | # define MAX_MEM_LEVEL 9 268 | # endif 269 | #endif 270 | 271 | /* Maximum value for windowBits in deflateInit2 and inflateInit2. 272 | * WARNING: reducing MAX_WBITS makes minigzip unable to extract .gz files 273 | * created by gzip. (Files created by minigzip can still be extracted by 274 | * gzip.) 275 | */ 276 | #ifndef MAX_WBITS 277 | # define MAX_WBITS 15 /* 32K LZ77 window */ 278 | #endif 279 | 280 | /* The memory requirements for deflate are (in bytes): 281 | (1 << (windowBits+2)) + (1 << (memLevel+9)) 282 | that is: 128K for windowBits=15 + 128K for memLevel = 8 (default values) 283 | plus a few kilobytes for small objects. For example, if you want to reduce 284 | the default memory requirements from 256K to 128K, compile with 285 | make CFLAGS="-O -DMAX_WBITS=14 -DMAX_MEM_LEVEL=7" 286 | Of course this will generally degrade compression (there's no free lunch). 287 | 288 | The memory requirements for inflate are (in bytes) 1 << windowBits 289 | that is, 32K for windowBits=15 (default value) plus about 7 kilobytes 290 | for small objects. 291 | */ 292 | 293 | /* Type declarations */ 294 | 295 | #ifndef OF /* function prototypes */ 296 | # ifdef STDC 297 | # define OF(args) args 298 | # else 299 | # define OF(args) () 300 | # endif 301 | #endif 302 | 303 | #ifndef Z_ARG /* function prototypes for stdarg */ 304 | # if defined(STDC) || defined(Z_HAVE_STDARG_H) 305 | # define Z_ARG(args) args 306 | # else 307 | # define Z_ARG(args) () 308 | # endif 309 | #endif 310 | 311 | /* The following definitions for FAR are needed only for MSDOS mixed 312 | * model programming (small or medium model with some far allocations). 313 | * This was tested only with MSC; for other MSDOS compilers you may have 314 | * to define NO_MEMCPY in zutil.h. If you don't need the mixed model, 315 | * just define FAR to be empty. 316 | */ 317 | #ifdef SYS16BIT 318 | # if defined(M_I86SM) || defined(M_I86MM) 319 | /* MSC small or medium model */ 320 | # define SMALL_MEDIUM 321 | # ifdef _MSC_VER 322 | # define FAR _far 323 | # else 324 | # define FAR far 325 | # endif 326 | # endif 327 | # if (defined(__SMALL__) || defined(__MEDIUM__)) 328 | /* Turbo C small or medium model */ 329 | # define SMALL_MEDIUM 330 | # ifdef __BORLANDC__ 331 | # define FAR _far 332 | # else 333 | # define FAR far 334 | # endif 335 | # endif 336 | #endif 337 | 338 | #if defined(WINDOWS) || defined(WIN32) 339 | /* If building or using zlib as a DLL, define ZLIB_DLL. 340 | * This is not mandatory, but it offers a little performance increase. 341 | */ 342 | # ifdef ZLIB_DLL 343 | # if defined(WIN32) && (!defined(__BORLANDC__) || (__BORLANDC__ >= 0x500)) 344 | # ifdef ZLIB_INTERNAL 345 | # define ZEXTERN extern __declspec(dllexport) 346 | # else 347 | # define ZEXTERN extern __declspec(dllimport) 348 | # endif 349 | # endif 350 | # endif /* ZLIB_DLL */ 351 | /* If building or using zlib with the WINAPI/WINAPIV calling convention, 352 | * define ZLIB_WINAPI. 353 | * Caution: the standard ZLIB1.DLL is NOT compiled using ZLIB_WINAPI. 354 | */ 355 | # ifdef ZLIB_WINAPI 356 | # ifdef FAR 357 | # undef FAR 358 | # endif 359 | # ifndef WIN32_LEAN_AND_MEAN 360 | # define WIN32_LEAN_AND_MEAN 361 | # endif 362 | # include 363 | /* No need for _export, use ZLIB.DEF instead. */ 364 | /* For complete Windows compatibility, use WINAPI, not __stdcall. */ 365 | # define ZEXPORT WINAPI 366 | # ifdef WIN32 367 | # define ZEXPORTVA WINAPIV 368 | # else 369 | # define ZEXPORTVA FAR CDECL 370 | # endif 371 | # endif 372 | #endif 373 | 374 | #if defined (__BEOS__) 375 | # ifdef ZLIB_DLL 376 | # ifdef ZLIB_INTERNAL 377 | # define ZEXPORT __declspec(dllexport) 378 | # define ZEXPORTVA __declspec(dllexport) 379 | # else 380 | # define ZEXPORT __declspec(dllimport) 381 | # define ZEXPORTVA __declspec(dllimport) 382 | # endif 383 | # endif 384 | #endif 385 | 386 | #ifndef ZEXTERN 387 | # define ZEXTERN extern 388 | #endif 389 | #ifndef ZEXPORT 390 | # define ZEXPORT 391 | #endif 392 | #ifndef ZEXPORTVA 393 | # define ZEXPORTVA 394 | #endif 395 | 396 | #ifndef FAR 397 | # define FAR 398 | #endif 399 | 400 | #if !defined(__MACTYPES__) 401 | typedef unsigned char Byte; /* 8 bits */ 402 | #endif 403 | typedef unsigned int uInt; /* 16 bits or more */ 404 | typedef unsigned long uLong; /* 32 bits or more */ 405 | 406 | #ifdef SMALL_MEDIUM 407 | /* Borland C/C++ and some old MSC versions ignore FAR inside typedef */ 408 | # define Bytef Byte FAR 409 | #else 410 | typedef Byte FAR Bytef; 411 | #endif 412 | typedef char FAR charf; 413 | typedef int FAR intf; 414 | typedef uInt FAR uIntf; 415 | typedef uLong FAR uLongf; 416 | 417 | #ifdef STDC 418 | typedef void const *voidpc; 419 | typedef void FAR *voidpf; 420 | typedef void *voidp; 421 | #else 422 | typedef Byte const *voidpc; 423 | typedef Byte FAR *voidpf; 424 | typedef Byte *voidp; 425 | #endif 426 | 427 | #if !defined(Z_U4) && !defined(Z_SOLO) && defined(STDC) 428 | # include 429 | # if (UINT_MAX == 0xffffffffUL) 430 | # define Z_U4 unsigned 431 | # elif (ULONG_MAX == 0xffffffffUL) 432 | # define Z_U4 unsigned long 433 | # elif (USHRT_MAX == 0xffffffffUL) 434 | # define Z_U4 unsigned short 435 | # endif 436 | #endif 437 | 438 | #ifdef Z_U4 439 | typedef Z_U4 z_crc_t; 440 | #else 441 | typedef unsigned long z_crc_t; 442 | #endif 443 | 444 | #if 1 /* was set to #if 1 by ./configure */ 445 | # define Z_HAVE_UNISTD_H 446 | #endif 447 | 448 | #if 1 /* was set to #if 1 by ./configure */ 449 | # define Z_HAVE_STDARG_H 450 | #endif 451 | 452 | #ifdef STDC 453 | # ifndef Z_SOLO 454 | # include /* for off_t */ 455 | # endif 456 | #endif 457 | 458 | #if defined(STDC) || defined(Z_HAVE_STDARG_H) 459 | # ifndef Z_SOLO 460 | # include /* for va_list */ 461 | # endif 462 | #endif 463 | 464 | #ifdef _WIN32 465 | # ifndef Z_SOLO 466 | # include /* for wchar_t */ 467 | # endif 468 | #endif 469 | 470 | /* a little trick to accommodate both "#define _LARGEFILE64_SOURCE" and 471 | * "#define _LARGEFILE64_SOURCE 1" as requesting 64-bit operations, (even 472 | * though the former does not conform to the LFS document), but considering 473 | * both "#undef _LARGEFILE64_SOURCE" and "#define _LARGEFILE64_SOURCE 0" as 474 | * equivalently requesting no 64-bit operations 475 | */ 476 | #if defined(_LARGEFILE64_SOURCE) && -_LARGEFILE64_SOURCE - -1 == 1 477 | # undef _LARGEFILE64_SOURCE 478 | #endif 479 | 480 | #ifndef Z_HAVE_UNISTD_H 481 | # ifdef __WATCOMC__ 482 | # define Z_HAVE_UNISTD_H 483 | # endif 484 | #endif 485 | #ifndef Z_HAVE_UNISTD_H 486 | # if defined(_LARGEFILE64_SOURCE) && !defined(_WIN32) 487 | # define Z_HAVE_UNISTD_H 488 | # endif 489 | #endif 490 | #ifndef Z_SOLO 491 | # if defined(Z_HAVE_UNISTD_H) 492 | # include /* for SEEK_*, off_t, and _LFS64_LARGEFILE */ 493 | # ifdef VMS 494 | # include /* for off_t */ 495 | # endif 496 | # ifndef z_off_t 497 | # define z_off_t off_t 498 | # endif 499 | # endif 500 | #endif 501 | 502 | #if defined(_LFS64_LARGEFILE) && _LFS64_LARGEFILE-0 503 | # define Z_LFS64 504 | #endif 505 | 506 | #if defined(_LARGEFILE64_SOURCE) && defined(Z_LFS64) 507 | # define Z_LARGE64 508 | #endif 509 | 510 | #if defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS-0 == 64 && defined(Z_LFS64) 511 | # define Z_WANT64 512 | #endif 513 | 514 | #if !defined(SEEK_SET) && !defined(Z_SOLO) 515 | # define SEEK_SET 0 /* Seek from beginning of file. */ 516 | # define SEEK_CUR 1 /* Seek from current position. */ 517 | # define SEEK_END 2 /* Set file pointer to EOF plus "offset" */ 518 | #endif 519 | 520 | #ifndef z_off_t 521 | # define z_off_t long 522 | #endif 523 | 524 | #if !defined(_WIN32) && defined(Z_LARGE64) 525 | # define z_off64_t off64_t 526 | #else 527 | # if defined(_WIN32) && !defined(__GNUC__) 528 | # define z_off64_t __int64 529 | # else 530 | # define z_off64_t z_off_t 531 | # endif 532 | #endif 533 | 534 | /* MVS linker does not support external names larger than 8 bytes */ 535 | #if defined(__MVS__) 536 | #pragma map(deflateInit_,"DEIN") 537 | #pragma map(deflateInit2_,"DEIN2") 538 | #pragma map(deflateEnd,"DEEND") 539 | #pragma map(deflateBound,"DEBND") 540 | #pragma map(inflateInit_,"ININ") 541 | #pragma map(inflateInit2_,"ININ2") 542 | #pragma map(inflateEnd,"INEND") 543 | #pragma map(inflateSync,"INSY") 544 | #pragma map(inflateSetDictionary,"INSEDI") 545 | #pragma map(compressBound,"CMBND") 546 | #pragma map(inflate_table,"INTABL") 547 | #pragma map(inflate_fast,"INFA") 548 | #pragma map(inflate_copyright,"INCOPY") 549 | #endif 550 | 551 | #endif /* ZCONF_H */ 552 | --------------------------------------------------------------------------------