text:
using LinearAlgebraicRepresentation
using Plasm
Lar = LinearAlgebraicRepresentation

W = [0.699559 0.531146 0.430114; 0.983132 0.633896 0.470059; 0.802309 0.324748 0.231597; 1.08588 0.427498 0.271541; 0.739504 0.332628 0.657186; 1.02308 0.435378 0.697131; 0.842254 0.126231 0.458669; 1.12583 0.228981 0.498614; 0.804524 0.465591 0.183914; 0.497724 0.561101 0.215202; 0.900034 0.773946 0.179168; 0.593233 0.869456 0.210456; 0.835812 0.460845 0.505202; 0.529011 0.556355 0.53649; 0.931322 0.7692 0.500456; 0.624521 0.864711 0.531744; 0.652935 0.383292 0.756213; 0.596398 0.32789 1.02843; 0.597534 0.65776 0.800566; 0.540996 0.602358 1.07278; 0.925154 0.427645 0.821777; 0.868616 0.372244 1.094; 0.869752 0.702113 0.86613; 0.813214 0.646711 1.13835; 0.731641 0.319559 0.0599335; 0.721369 0.329819 0.26783; 0.741902 0.527481 0.0501789; 0.731629 0.537741 0.258076; 0.939538 0.309804 0.0706877; 0.929265 0.320065 0.278585; 0.949799 0.517726 0.0609331; 0.939526 0.527987 0.26883; 0.627886 0.492434 0.5694; 0.419634 0.662086 0.807705; 0.797538 0.800784 0.498138; 0.589286 0.970436 0.736444; 0.866191 0.421173 0.828383; 0.65794 0.590824 1.06669; 1.03584 0.729523 0.757122; 0.827591 0.899175 0.995427; 0.486433 0.862932 0.422152; 0.580642 0.825122 0.43872; 0.448623 0.800467 0.494594; 0.542832 0.762656 0.511161; 0.503001 0.935374 0.493265; 0.59721 0.897564 0.509833; 0.465191 0.872908 0.565707; 0.559399 0.835098 0.582275; 0.157343 0.257051 0.117692; -0.110249 0.200736 0.129815; 0.101028 0.524914 0.118954; -0.166564 0.4686 0.131077; 0.169466 0.258312 0.391143; -0.0981264 0.201997 0.403266; 0.113151 0.526176 0.392405; -0.154441 0.469861 0.404527; -0.0025703 0.230563 0.288724; 0.149874 0.362901 0.520169; 0.129768 0.424447 0.0906967; 0.282212 0.556785 0.322141; 0.228874 0.0325352 0.24951; 0.381319 0.164873 0.480955; 0.361212 0.226419 0.0514828; 0.513657 0.358757 0.282927; 0.486895 0.366207 0.591684; 0.567037 0.42858 0.668996; 0.549269 0.411922 0.490146; 0.62941 0.474295 0.567458; 0.564207 0.264669 0.59346; 0.644349 0.327042 0.670772; 0.626581 0.310384 0.491922; 0.706722 0.372757 0.569234; 0.972103 0.340237 0.493184; 0.855325 0.233767 0.729488; 0.865633 0.596247 0.555917; 0.748855 0.489777 0.792221; 1.20841 0.40297 0.638227; 1.09163 0.2965 0.874531; 1.10194 0.65898 0.70096; 0.985159 0.55251 0.937264; 1.11852 0.716117 0.64975; 0.909154 0.589128 0.746627; 0.991533 0.945338 0.675775; 0.782165 0.81835 0.772652; 1.2154 0.742142 0.893232; 1.00603 0.615153 0.990109; 1.08841 0.971363 0.919258; 0.879042 0.844375 1.01613; 0.572105 0.803861 0.763926; 0.565967 0.809998 1.01466; 0.578242 1.0546 0.75794; 0.572103 1.06073 1.00867; 0.822837 0.797875 0.770211; 0.816699 0.804011 1.02094; 0.828974 1.04861 0.764225; 0.822835 1.05475 1.01496; 0.289216 0.893982 0.871068; 0.441361 0.978796 0.928016; 0.374029 0.846063 0.715842; 0.526174 0.930877 0.77279; 0.346164 0.738756 0.950102; 0.498309 0.823569 1.00705; 0.430977 0.690837 0.794876; 0.583122 0.775651 0.851824; -0.0744743 0.724381 0.187024; 0.119585 0.655693 0.21302; -0.143163 0.58063 0.319954; 0.0508966 0.511942 0.34595; -0.0484785 0.857312 0.344207; 0.145581 0.788623 0.370203; -0.117167 0.713561 0.477138; 0.0768923 0.644872 0.503134; 0.769085 0.260815 0.361719; 0.520481 0.314402 0.373548; 0.822671 0.509698 0.360459; 0.574067 0.563284 0.372288; 0.780913 0.259556 0.61603; 0.532309 0.313143 0.627859; 0.8345 0.508438 0.61477; 0.585896 0.562025 0.626599]

cop_EW = sparse([1, 5, 9, 1, 6, 10, 2, 5, 11, 2, 6, 12, 3, 7, 9, 3, 8, 10, 4, 7, 11, 4, 8, 12, 13, 17, 21, 13, 18, 22, 14, 17, 23, 14, 18, 24, 15, 19, 21, 15, 20, 22, 16, 19, 23, 16, 20, 24, 25, 29, 33, 25, 30, 34, 26, 29, 35, 26, 30, 36, 27, 31, 33, 27, 32, 34, 28, 31, 35, 28, 32, 36, 37, 41, 45, 37, 42, 46, 38, 41, 47, 38, 42, 48, 39, 43, 45, 39, 44, 46, 40, 43, 47, 40, 44, 48, 49, 53, 57, 49, 54, 58, 50, 53, 59, 50, 54, 60, 51, 55, 57, 51, 56, 58, 52, 55, 59, 52, 56, 60, 61, 65, 69, 61, 66, 70, 62, 65, 71, 62, 66, 72, 63, 67, 69, 63, 68, 70, 64, 67, 71, 64, 68, 72, 73, 77, 81, 73, 78, 82, 74, 77, 83, 74, 78, 84, 75, 79, 81, 75, 80, 82, 76, 79, 83, 76, 80, 84, 85, 89, 93, 85, 90, 94, 86, 89, 95, 86, 90, 96, 87, 91, 93, 87, 92, 94, 88, 91, 95, 88, 92, 96, 97, 101, 105, 97, 102, 106, 98, 101, 107, 98, 102, 108, 99, 103, 105, 99, 104, 106, 100, 103, 107, 100, 104, 108, 109, 113, 117, 109, 114, 118, 110, 113, 119, 110, 114, 120, 111, 115, 117, 111, 116, 118, 112, 115, 119, 112, 116, 120, 121, 125, 129, 121, 126, 130, 122, 125, 131, 122, 126, 132, 123, 127, 129, 123, 128, 130, 124, 127, 131, 124, 128, 132, 133, 137, 141, 133, 138, 142, 134, 137, 143, 134, 138, 144, 135, 139, 141, 135, 140, 142, 136, 139, 143, 136, 140, 144, 145, 149, 153, 145, 150, 154, 146, 149, 155, 146, 150, 156, 147, 151, 153, 147, 152, 154, 148, 151, 155, 148, 152, 156, 157, 161, 165, 157, 162, 166, 158, 161, 167, 158, 162, 168, 159, 163, 165, 159, 164, 166, 160, 163, 167, 160, 164, 168, 169, 173, 177, 169, 174, 178, 170, 173, 179, 170, 174, 180, 171, 175, 177, 171, 176, 178, 172, 175, 179, 172, 176, 180], [1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 5, 6, 6, 6, 7, 7, 7, 8, 8, 8, 9, 9, 9, 10, 10, 10, 11, 11, 11, 12, 12, 12, 13, 13, 13, 14, 14, 14, 15, 15, 15, 16, 16, 16, 17, 17, 17, 18, 18, 18, 19, 19, 19, 20, 20, 20, 21, 21, 21, 22, 22, 22, 23, 23, 23, 24, 24, 24, 25, 25, 25, 26, 26, 26, 27, 27, 27, 28, 28, 28, 29, 29, 29, 30, 30, 30, 31, 31, 31, 32, 32, 32, 33, 33, 33, 34, 34, 34, 35, 35, 35, 36, 36, 36, 37, 37, 37, 38, 38, 38, 39, 39, 39, 40, 40, 40, 41, 41, 41, 42, 42, 42, 43, 43, 43, 44, 44, 44, 45, 45, 45, 46, 46, 46, 47, 47, 47, 48, 48, 48, 49, 49, 49, 50, 50, 50, 51, 51, 51, 52, 52, 52, 53, 53, 53, 54, 54, 54, 55, 55, 55, 56, 56, 56, 57, 57, 57, 58, 58, 58, 59, 59, 59, 60, 60, 60, 61, 61, 61, 62, 62, 62, 63, 63, 63, 64, 64, 64, 65, 65, 65, 66, 66, 66, 67, 67, 67, 68, 68, 68, 69, 69, 69, 70, 70, 70, 71, 71, 71, 72, 72, 72, 73, 73, 73, 74, 74, 74, 75, 75, 75, 76, 76, 76, 77, 77, 77, 78, 78, 78, 79, 79, 79, 80, 80, 80, 81, 81, 81, 82, 82, 82, 83, 83, 83, 84, 84, 84, 85, 85, 85, 86, 86, 86, 87, 87, 87, 88, 88, 88, 89, 89, 89, 90, 90, 90, 91, 91, 91, 92, 92, 92, 93, 93, 93, 94, 94, 94, 95, 95, 95, 96, 96, 96, 97, 97, 97, 98, 98, 98, 99, 99, 99, 100, 100, 100, 101, 101, 101, 102, 102, 102, 103, 103, 103, 104, 104, 104, 105, 105, 105, 106, 106, 106, 107, 107, 107, 108, 108, 108, 109, 109, 109, 110, 110, 110, 111, 111, 111, 112, 112, 112, 113, 113, 113, 114, 114, 114, 115, 115, 115, 116, 116, 116, 117, 117, 117, 118, 118, 118, 119, 119, 119, 120, 120, 120], Int8[-1, -1, -1, 1, -1, -1, -1, 1, -1, 1, 1, -1, -1, -1, 1, 1, -1, 1, -1, 1, 1, 1, 1, 1, -1, -1, -1, 1, -1, -1, -1, 1, -1, 1, 1, -1, -1, -1, 1, 1, -1, 1, -1, 1, 1, 1, 1, 1, -1, -1, -1, 1, -1, -1, -1, 1, -1, 1, 1, -1, -1, -1, 1, 1, -1, 1, -1, 1, 1, 1, 1, 1, -1, -1, -1, 1, -1, -1, -1, 1, -1, 1, 1, -1, -1, -1, 1, 1, -1, 1, -1, 1, 1, 1, 1, 1, -1, -1, -1, 1, -1, -1, -1, 1, -1, 1, 1, -1, -1, -1, 1, 1, -1, 1, -1, 1, 1, 1, 1, 1, -1, -1, -1, 1, -1, -1, -1, 1, -1, 1, 1, -1, -1, -1, 1, 1, -1, 1, -1, 1, 1, 1, 1, 1, -1, -1, -1, 1, -1, -1, -1, 1, -1, 1, 1, -1, -1, -1, 1, 1, -1, 1, -1, 1, 1, 1, 1, 1, -1, -1, -1, 1, -1, -1, -1, 1, -1, 1, 1, -1, -1, -1, 1, 1, -1, 1, -1, 1, 1, 1, 1, 1, -1, -1, -1, 1, -1, -1, -1, 1, -1, 1, 1, -1, -1, -1, 1, 1, -1, 1, -1, 1, 1, 1, 1, 1, -1, -1, -1, 1, -1, -1, -1, 1, -1, 1, 1, -1, -1, -1, 1, 1, -1, 1, -1, 1, 1, 1, 1, 1, -1, -1, -1, 1, -1, -1, -1, 1, -1, 1, 1, -1, -1, -1, 1, 1, -1, 1, -1, 1, 1, 1, 1, 1, -1, -1, -1, 1, -1, -1, -1, 1, -1, 1, 1, -1, -1, -1, 1, 1, -1, 1, -1, 1, 1, 1, 1, 1, -1, -1, -1, 1, -1, -1, -1, 1, -1, 1, 1, -1, -1, -1, 1, 1, -1, 1, -1, 1, 1, 1, 1, 1, -1, -1, -1, 1, -1, -1, -1, 1, -1, 1, 1, -1, -1, -1, 1, 1, -1, 1, -1, 1, 1, 1, 1, 1, -1, -1, -1, 1, -1, -1, -1, 1, -1, 1, 1, -1, -1, -1, 1, 1, -1, 1, -1, 1, 1, 1, 1, 1])

cop_FE = sparse([1, 3, 1, 4, 2, 3, 2, 4, 1, 5, 1, 6, 2, 5, 2, 6, 3, 5, 3, 6, 4, 5, 4, 6, 7, 9, 7, 10, 8, 9, 8, 10, 7, 11, 7, 12, 8, 11, 8, 12, 9, 11, 9, 12, 10, 11, 10, 12, 13, 15, 13, 16, 14, 15, 14, 16, 13, 17, 13, 18, 14, 17, 14, 18, 15, 17, 15, 18, 16, 17, 16, 18, 19, 21, 19, 22, 20, 21, 20, 22, 19, 23, 19, 24, 20, 23, 20, 24, 21, 23, 21, 24, 22, 23, 22, 24, 25, 27, 25, 28, 26, 27, 26, 28, 25, 29, 25, 30, 26, 29, 26, 30, 27, 29, 27, 30, 28, 29, 28, 30, 31, 33, 31, 34, 32, 33, 32, 34, 31, 35, 31, 36, 32, 35, 32, 36, 33, 35, 33, 36, 34, 35, 34, 36, 37, 39, 37, 40, 38, 39, 38, 40, 37, 41, 37, 42, 38, 41, 38, 42, 39, 41, 39, 42, 40, 41, 40, 42, 43, 45, 43, 46, 44, 45, 44, 46, 43, 47, 43, 48, 44, 47, 44, 48, 45, 47, 45, 48, 46, 47, 46, 48, 49, 51, 49, 52, 50, 51, 50, 52, 49, 53, 49, 54, 50, 53, 50, 54, 51, 53, 51, 54, 52, 53, 52, 54, 55, 57, 55, 58, 56, 57, 56, 58, 55, 59, 55, 60, 56, 59, 56, 60, 57, 59, 57, 60, 58, 59, 58, 60, 61, 63, 61, 64, 62, 63, 62, 64, 61, 65, 61, 66, 62, 65, 62, 66, 63, 65, 63, 66, 64, 65, 64, 66, 67, 69, 67, 70, 68, 69, 68, 70, 67, 71, 67, 72, 68, 71, 68, 72, 69, 71, 69, 72, 70, 71, 70, 72, 73, 75, 73, 76, 74, 75, 74, 76, 73, 77, 73, 78, 74, 77, 74, 78, 75, 77, 75, 78, 76, 77, 76, 78, 79, 81, 79, 82, 80, 81, 80, 82, 79, 83, 79, 84, 80, 83, 80, 84, 81, 83, 81, 84, 82, 83, 82, 84, 85, 87, 85, 88, 86, 87, 86, 88, 85, 89, 85, 90, 86, 89, 86, 90, 87, 89, 87, 90, 88, 89, 88, 90], [1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 14, 14, 15, 15, 16, 16, 17, 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 22, 23, 23, 24, 24, 25, 25, 26, 26, 27, 27, 28, 28, 29, 29, 30, 30, 31, 31, 32, 32, 33, 33, 34, 34, 35, 35, 36, 36, 37, 37, 38, 38, 39, 39, 40, 40, 41, 41, 42, 42, 43, 43, 44, 44, 45, 45, 46, 46, 47, 47, 48, 48, 49, 49, 50, 50, 51, 51, 52, 52, 53, 53, 54, 54, 55, 55, 56, 56, 57, 57, 58, 58, 59, 59, 60, 60, 61, 61, 62, 62, 63, 63, 64, 64, 65, 65, 66, 66, 67, 67, 68, 68, 69, 69, 70, 70, 71, 71, 72, 72, 73, 73, 74, 74, 75, 75, 76, 76, 77, 77, 78, 78, 79, 79, 80, 80, 81, 81, 82, 82, 83, 83, 84, 84, 85, 85, 86, 86, 87, 87, 88, 88, 89, 89, 90, 90, 91, 91, 92, 92, 93, 93, 94, 94, 95, 95, 96, 96, 97, 97, 98, 98, 99, 99, 100, 100, 101, 101, 102, 102, 103, 103, 104, 104, 105, 105, 106, 106, 107, 107, 108, 108, 109, 109, 110, 110, 111, 111, 112, 112, 113, 113, 114, 114, 115, 115, 116, 116, 117, 117, 118, 118, 119, 119, 120, 120, 121, 121, 122, 122, 123, 123, 124, 124, 125, 125, 126, 126, 127, 127, 128, 128, 129, 129, 130, 130, 131, 131, 132, 132, 133, 133, 134, 134, 135, 135, 136, 136, 137, 137, 138, 138, 139, 139, 140, 140, 141, 141, 142, 142, 143, 143, 144, 144, 145, 145, 146, 146, 147, 147, 148, 148, 149, 149, 150, 150, 151, 151, 152, 152, 153, 153, 154, 154, 155, 155, 156, 156, 157, 157, 158, 158, 159, 159, 160, 160, 161, 161, 162, 162, 163, 163, 164, 164, 165, 165, 166, 166, 167, 167, 168, 168, 169, 169, 170, 170, 171, 171, 172, 172, 173, 173, 174, 174, 175, 175, 176, 176, 177, 177, 178, 178, 179, 179, 180, 180], Int8[1, 1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, -1, -1, 1, -1, -1, -1, 1, -1, -1, 1, 1, 1, 1, 1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, -1, -1, 1, -1, -1, -1, 1, -1, -1, 1, 1, 1, 1, 1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, -1, -1, 1, -1, -1, -1, 1, -1, -1, 1, 1, 1, 1, 1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, -1, -1, 1, -1, -1, -1, 1, -1, -1, 1, 1, 1, 1, 1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, -1, -1, 1, -1, -1, -1, 1, -1, -1, 1, 1, 1, 1, 1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, -1, -1, 1, -1, -1, -1, 1, -1, -1, 1, 1, 1, 1, 1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, -1, -1, 1, -1, -1, -1, 1, -1, -1, 1, 1, 1, 1, 1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, -1, -1, 1, -1, -1, -1, 1, -1, -1, 1, 1, 1, 1, 1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, -1, -1, 1, -1, -1, -1, 1, -1, -1, 1, 1, 1, 1, 1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, -1, -1, 1, -1, -1, -1, 1, -1, -1, 1, 1, 1, 1, 1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, -1, -1, 1, -1, -1, -1, 1, -1, -1, 1, 1, 1, 1, 1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, -1, -1, 1, -1, -1, -1, 1, -1, -1, 1, 1, 1, 1, 1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, -1, -1, 1, -1, -1, -1, 1, -1, -1, 1, 1, 1, 1, 1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, -1, -1, 1, -1, -1, -1, 1, -1, -1, 1, 1, 1, 1, 1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, -1, -1, 1, -1, -1, -1, 1, -1, -1, 1, 1, 1])


text:
# Find llvm
if haskey(ENV,"LLVM_DIR")
    llvmdir = ENV["LLVM_DIR"]
else
    try
        llvmdir = readchomp(`llvm-config --includedir`)
    catch
        try
            llvmdir = readchomp(`$(joinpath(JULIA_HOME,"llvm-config")) --includedir`)
        catch
            error("Could not find LLVM include directory. Set LLVM_DIR env variable manually")
        end
    end
end

import Base.Intrinsics.llvmcall

Base.t_func[eval(Core.Intrinsics,:llvmcall)] =
(3, Inf,function(args, fptrt, rtt, att, a...)
    fptr, rt, at = args
    ret = None
    if is(rt,Type{Void})
        ret = Nothing
    elseif Base.isType(rtt)
        ret = rtt.parameters[1]
    elseif isa(rtt,Tuple) && is(rtt[1],Function)
        if isa(rt,Expr)
            if rt.head == :call
                if isa(rt.args[1],Expr) && rt.args[1].head == :call &&
                    rt.args[1].args[1] == :top && rt.args[1].args[2] == :tuple
                    rt = rt.args[2:end]
                end
            elseif rt.head == :call1
                if isa(rt.args[1],TopNode) && rt.args[1].name == :tuple
                    rt = rt.args[2:end]
                end
            end
        end
        if (isa(rt,Tuple) || isa(rt,Array)) && isa(rt[1],Function)
            ret = rt[1](a,rt[2:end]...)
        end
    end
    if ret === None
        if isa(rt,Type) && isa(rt,Tuple)
            ret = rtt
        else
            ret = Any
        end
    end
    return ret
end)

#foo() = llvmcall(compile_func,Void,(Ptr{Uint8},),"SUCCESS")
#bar() = ccall(:jl_error,Void,(Ptr{Uint8},),"SUCCESS")

#code_llvm(foo,())
#code_llvm(bar,())

initialized = false
function initialize(mod,f)
    global initialized
    was_initialized = initialized
    if !initialized
        initialized = true
        ccall(:init_julia_clang_env,Void,(Ptr{Void},),mod)
    end
    state = ccall(:setup_cpp_env,Ptr{Void},(Ptr{Void},Ptr{Void}),mod,f)
    if !was_initialized
        ccall(:init_header,Void,(Ptr{Uint8},),joinpath(dirname(Base.source_path()),"test.h"))
    end
    state
end

function initialize(thunk::Function,mod,f)
    state=initialize(mod,f)
    thunk()
    cleanup(state)
end

cleanup(state) = ccall(:cleanup_cpp_env,Ptr{Void},(Ptr{Void},),state)

immutable CppPtr{T,targs}
    ptr::Ptr{Void}
end

immutable CppRef{T,targs}
    ptr::Ptr{Void}
end

immutable CppValue{T,targs}
    data::Vector{Uint8}
end

immutable CppPOD{T,N}
    data::NTuple{N,Uint8}
    CppPOD() = new()
end

immutable CppCast{T,To}
    from::T
end
CppCast{T,To}(from::T,::Type{To}) = CppCast{T,To}(from)

macro pcpp_str(s)
    CppPtr{symbol(s),()}
end

macro rcpp_str(s)
    CppRef{symbol(s),()}
end

macro vcpp_str(s)
    CppValue{symbol(s),()}
end

cast{T,To}(from::T,::Type{To}) = CppCast{T,To}(from)

import Base: cconvert

cconvert(::Type{Ptr{Void}},p::CppPtr) = p.ptr
cconvert(::Type{Ptr{Void}},p::CppRef) = p.ptr

function compile_new(f,mod,argt,t)
    initialize(mod,f) do
        d = lookup_name(t)
        ccall(:emit_cpp_new,Void,(Ptr{Void},),d)
    end
end
macro cppnew(t)
    :( CppPtr{$(quot(symbol(t)))}(llvmcall((compile_new,$t),Ptr{Void},()))::$(CppPtr{symbol(t)}) )
end

import Base: ==

==(p1::CppPtr,p2::Ptr) = p1.ptr == p2
==(p1::Ptr,p2::CppPtr) = p1 == p2.ptr

function cpp_arg(T)
    return T
end

function get_decl(argt,func,thiscall)
    if thiscall
        thist = argt[1]
        if !(thist <: CppPtr) && !(thist <: CppRef) && !(thist <: CppValue)
            error("Expected a CppPtr, CppRef or CppValue type, got $thist")
        end
        fname = string(thist.parameters[1],"::",func)
    else
        fname = string(func)
    end
    #println(fname)
    lookup_name(fname)
end

function cpp_argt(args,uuid,fname,thiscall)
    #println("cpp_argt")
    for i = 1:length(args)
        arg = args[i]
        if arg == None
            error("Cannot determine type for argument $(i-int(thiscall)) to function $fname. Please add a type annotation.")
        end
    end
    args
end

ldump(x) = llvmcall((compile_func,"clang::Decl::dumpColor"),Void,(Ptr{Void},),x)
ddump(x) = llvmcall((compile_func,"clang::DeclContext::dump"),Void,(Ptr{Void},),x)
function primary_decl(x)
    x == C_NULL && error("primary_decl")
    y = decl_context(x)
    ccall(:get_primary_dc,Ptr{Void},(Ptr{Void},),y)
end
function decl_context(x)
    x == C_NULL && error("decl_context")
    y = ccall(:decl_context,Ptr{Void},(Ptr{Void},),x)
    if y == C_NULL
        #ccall(:cdump,Void,(Ptr{Void},),x)
        error("Decl Context Returned NULL!")
    end
    y
end
function to_decl(x)
    x == C_NULL && error("to_decl")
    ccall(:to_decl,Ptr{Void},(Ptr{Void},),x)
end
#map(x->ldump(decl(x)),)

#c = ClangCompiler()

using Base.Meta

function build_cpp_call1(cexpr,member,ret,prefix,this,uuid)
    @assert isexpr(cexpr,:call)
    fname = string(prefix,cexpr.args[1])
    c = Expr(:call,:llvmcall,Expr(:tuple,member,uuid,fname,this != nothing),
                             Expr(:tuple,ret,uuid,fname,this != nothing),
                             Expr(:tuple,cpp_argt,uuid,fname,this != nothing))
    if this != nothing
        push!(c.args,this)
    end
    if length(cexpr.args) > 1
        for arg in cexpr.args[2:end]
            push!(c.args,arg)
        end
    end
    return c
end

function build_ref(expr)
    if isa(expr,Symbol)
        return (expr,)
    elseif isexpr(expr,:(.))
        return tuple(build_ref(expr.args[1])...,build_ref(expr.args[2])...)
    else
        return (expr,)
    end
end

function cpp_ref(expr, prefix, uuid)
    fname = string(prefix,expr)
    c = Expr(:call,:llvmcall,Expr(:tuple,:cpp_ref_ref,uuid,fname),
                         Expr(:tuple,:cpp_ref_ret,uuid,fname),
                         ())
    return c
end

function cpps_impl(expr,member,build_cpp_call,ret,prefix="",uuid = rand(Uint128))
    # Expands a->b to
    # llvmcall((cpp_member,typeof(a),"b"))
    if isa(expr,Symbol)
        error("Unfinished")
        return cpp_ref(expr,prefix,uuid)
    elseif expr.head == :(->)
        a = expr.args[1]
        b = expr.args[2]
        i = 1
        while !(isexpr(b,:call) || isa(b,Symbol))
            b = expr.args[2].args[i]
            if !(isexpr(b,:call) || isexpr(b,:line) || isa(b,Symbol))
                error("Malformed C++ call. Expected member not $b")
            end
            i += 1
        end
        if isexpr(b,:call)
            return build_cpp_call(b,member,ret,prefix,a,uuid)
        else
            Base.dump(expr)
            error("Unsupported!")
        end
    elseif isexpr(expr,:(=))
        flattened = build_ref(expr.args[1])
        return Expr(:call,:llvmcall,Expr(:tuple,:cpp_assignment,uuid,quot(flattened[2:end])),
                Void,Expr(:tuple,:cpp_argt),flattened[1],expr.args[2])
    elseif isexpr(expr,:(::))
        return cpps_impl(expr.args[2],member,build_cpp_call,ret,string(to_prefix(expr.args[1]),"::"),uuid)
    elseif isexpr(expr,:call)
        return build_cpp_call(expr,member,ret,prefix,nothing,uuid)
    end
    error("Unrecognized CPP Expression ",expr," (",expr.head,")")
end

# STAGE 1 BOOTSTRAP

function process_args(f,argt)
    args = Array(Ptr{Void},length(argt))
    types = Array(Ptr{Void},length(argt))
    for i in 1:length(argt)
        t = argt[i]
        args[i] = ccall(:get_nth_argument,Ptr{Void},(Ptr{Void},Csize_t),f,i-1)
        if t <: CppPtr || t <: CppRef
            types[i] = C_NULL
            args[i] = ccall(:create_extract_value,Ptr{Void},(Ptr{Void},Csize_t),args[i],0)
        else
            types[i] = C_NULL
        end
        if args[i] == C_NULL
            println((i,t))
            #error("Failed to process argument")
        end
    end
    args, types
end

function compile_func(f,mod,argt,func)
    initialize(mod,f) do
        d = lookup_name(func)
        args,types = process_args(f,argt)
        ccall(:emit_cpp_call,Void,(Ptr{Void},Ptr{Ptr{Void}},Ptr{Ptr{Void}},Ptr{Ptr{Void}},Csize_t,Bool,Bool,Ptr{Void}),d,args,types,C_NULL,length(args),false,true,C_NULL)
    end
end

function check_args(argt,f)
    for (i,t) in enumerate(argt)
        if isa(t,UnionType) || (isa(t,DataType) && t.abstract) ||
            (!(t <: CppPtr) && !(t <: CppRef) && !(t <: CppValue) && !(t <: CppCast) && !in(t,
                [Bool, Uint8, Uint32, Uint64, Int64, Ptr{Void}, Ptr{Uint8}]))
            error("Got bad type information while compiling $f (got $t for argument $i)")
        end
    end
end

function cpp_rett(args,uuid,f,thiscall)
    check_args(args,f)
    d = get_decl(args,f,thiscall)
    t = ccall(:get_result_type,Ptr{Void},(Ptr{Void},),d)
    arr = Array(Ptr{Void},1)
    if llvmcall((compile_func,"clang::Type::isVoidType"),Bool,(Ptr{Void},),t) != 0
        return Void
    end
    if llvmcall((compile_func,"clang::Type::isBooleanType"),Bool,(Ptr{Void},),t) != 0
        return Bool
    end
    if llvmcall((compile_func,"clang::Type::isIntegerType"),Bool,(Ptr{Void},),t) != 0
        # This is wrong. Might be any int. Need to access the BuiltinType::Kind Enum
        return Int
    end
    return Ptr{Void}
end

function cpp_member(f,mod,argt,uuid,func,thiscall)
    #println((f,mod,argt,func,thiscall))
    initialize(mod,f) do
        d = get_decl(argt,func,thiscall)
        args,types = process_args(f,argt)
        ccall(:emit_cpp_call,Ptr{Void},(Ptr{Void},Ptr{Ptr{Void}},Ptr{Ptr{Void}},Ptr{Ptr{Void}},Csize_t,Bool,Bool,Ptr{Void}),d,args,types,C_NULL,length(args),false,true,C_NULL)
    end
end

macro cpp1(expr)
    cpps_impl(expr,cpp_member,build_cpp_call1,cpp_rett)
end

# STAGE 2 BOOTSTRAP

type ClangCompiler
    handle::pcpp"clang::CompilerInstance"
    cgm::pcpp"clang::CodeGen::CodeGenModule"
    cgt::pcpp"clang::CodeGen::CodeGenTypes"
    cgf::pcpp"clang::CodeGen::CodeGenFunction"
    function ClangCompiler(handle::pcpp"clang::CompilerInstance",cgm::pcpp"clang::CodeGen::CodeGenModule",cgt::pcpp"clang::CodeGen::CodeGenTypes",cgf::pcpp"clang::CodeGen::CodeGenFunction")
        new(handle,cgm,cgt,cgf)
    end
end
const default_compiler = ClangCompiler(
    (pcpp"clang::CompilerInstance")(ccall(:clang_get_instance,Ptr{Void},())),
    (pcpp"clang::CodeGen::CodeGenModule")(ccall(:clang_get_cgm,Ptr{Void},())),
    (pcpp"clang::CodeGen::CodeGenTypes")(ccall(:clang_get_cgt,Ptr{Void},())),
    (pcpp"clang::CodeGen::CodeGenFunction")(ccall(:clang_get_cgf,Ptr{Void},()))
)

lookup_name2(fname) = pcpp"clang::Decl"(lookup_name(fname))
function tocxx(decl::pcpp"clang::Decl")
    @assert decl.ptr != C_NULL
    pcpp"clang::CXXRecordDecl"(ccall(:to_cxxdecl,Ptr{Void},(Ptr{Void},),decl.ptr))
end

RequireCompleteType(d::pcpp"clang::Type") = ccall(:RequireCompleteType,Cint,(Ptr{Void},),d.ptr) > 0

function typeForDecl(cxxdecl::pcpp"clang::CXXRecordDecl")
    @assert cxxdecl.ptr != C_NULL
    pcpp"clang::Type"(@cpp1 cxxdecl->getTypeForDecl())
end
typeForDecl(p::pcpp"clang::Decl") = typeForDecl(tocxx(p))
typeForDecl(p::pcpp"clang::ClassTemplateSpecializationDecl") = typeForDecl(pcpp"clang::CXXRecordDecl"(p.ptr))

function sizeof(d::pcpp"clang::CXXRecordDecl")
    @assert d.ptr != C_NULL
    executionEngine = pcpp"llvm::ExecutionEngine"(ccall(:jl_get_llvm_ee,Ptr{Void},()))
    cgt = pcpp"clang::CodeGen::CodeGenTypes"(ccall(:clang_get_cgt,Ptr{Void},()))
    dl = @cpp1 executionEngine->getDataLayout()
    RequireCompleteType(typeForDecl(d))
    t = pcpp"llvm::Type"(@cpp1 cgt->ConvertRecordDeclType(d))
    @assert @cpp1 t->isSized()
    div((@cpp1 dl->getTypeSizeInBits(t)),8)
end

sizeof(d::pcpp"clang::Decl") = sizeof(tocxx(d))

dump(x::CppPtr) = @cpp1 x->dump()
dump(x::pcpp"clang::Decl") = @cpp1 x->dumpColor()
dump(x::pcpp"clang::Expr") = @cpp1 x->dumpColor()

pointerTo(t::pcpp"clang::Type") = pcpp"clang::Type"(ccall(:getPointerTo,Ptr{Void},(Ptr{Void},),t.ptr))
referenceTo(t::pcpp"clang::Type") = pcpp"clang::Type"(ccall(:getReferenceTo,Ptr{Void},(Ptr{Void},),t.ptr))
createDeref(p::pcpp"clang::Type", val::pcpp"llvm::UndefValue") = pcpp"clang::Expr"(ccall(:createDeref,Ptr{Void},(Ptr{Void},Ptr{Void}),p.ptr,val.ptr))

tovdecl(p::pcpp"clang::Decl") = pcpp"clang::ValueDecl"(ccall(:tovdecl,Ptr{Void},(Ptr{Void},),p.ptr))
CreateDeclRefExpr(p::pcpp"clang::ValueDecl",ty::pcpp"clang::Type") = pcpp"clang::Expr"(ccall(:CreateDeclRefExpr,Ptr{Void},(Ptr{Void},Ptr{Void}),p.ptr,ty.ptr))
CreateDeclRefExpr(p::pcpp"clang::Decl",ty::pcpp"clang::Type") = CreateDeclRefExpr(tovdecl(p),ty)

CreateParmVarDecl(p::pcpp"clang::Type") = pcpp"clang::Decl"(ccall(:CreateParmVarDecl,Ptr{Void},(Ptr{Void},),p.ptr))

CreateMemberExpr(base::pcpp"clang::Expr",isarrow::Bool,member::pcpp"clang::ValueDecl") = pcpp"clang::Expr"(ccall(:CreateMemberExpr,Ptr{Void},(Ptr{Void},Cint,Ptr{Void}),base.ptr,isarrow,member.ptr))
BuildCallToMemberFunction(me::pcpp"clang::Expr", args::Vector{pcpp"clang::Expr"}) = pcpp"clang::Expr"(ccall(:build_call_to_member,Ptr{Void},(Ptr{Void},Ptr{Ptr{Void}},Csize_t),
        me.ptr,[arg.ptr for arg in args],length(args)))

BuildCXXTypeConstructExpr(t::pcpp"clang::Type", exprs::Vector{pcpp"clang::Expr"}) = pcpp"clang::Expr"(ccall(:typeconstruct,Ptr{Void},(Ptr{Void},Ptr{Ptr{Void}},Csize_t),t,[expr.ptr for expr in exprs],length(exprs)))

code_llvmf(f,t) = pcpp"llvm::Function"(ccall(:jl_get_llvmf, Ptr{Void}, (Any,Any,Bool), f, t, false))

macro code_llvmf(ex0)
    gen_call_with_extracted_types(code_llvmf, ex0)
end

create_extract_value(v::pcpp"llvm::Value",idx) = pcpp"llvm::Value"(ccall(:create_extract_value,Ptr{Void},(Ptr{Void},Csize_t),v.ptr,idx))

tollvmty(t::pcpp"clang::Type") = pcpp"llvm::Type"(ccall(:tollvmty,Ptr{Void},(Ptr{Void},),t.ptr))
getPointerTo(t::pcpp"llvm::Type") = pcpp"llvm::Type"(@cpp1 t->getPointerTo())
getType(v::pcpp"llvm::Value") = pcpp"llvm::Type"(@cpp1 v->getType())
CreateBitCast(builder,data::pcpp"llvm::Value",destty::pcpp"llvm::Type") = pcpp"llvm::Value"(@cpp1 builder->CreateBitCast(data,destty))
CreateLoad(builder,val::pcpp"llvm::Value") = pcpp"llvm::Value"(ccall(:createLoad,Ptr{Void},(Ptr{Void},Ptr{Void}),builder.ptr,val.ptr))

#TODO: Figure out why the first definition doesn't work
#CreateConstGEP1_32(builder,x::pcpp"llvm::Value",idx) = pcpp"llvm::Value"(@cpp1 builder->CreateConstGEP1_32(x,uint32(idx)))
CreateConstGEP1_32(builder,x,idx) = pcpp"llvm::Value"(ccall(:CreateConstGEP1_32,Ptr{Void},(Ptr{Void},Ptr{Void},Uint32),builder,x,uint32(idx)))

function _decl_name(d)
    if d == C_NULL
        error("get_name failed")
    end
    s = ccall(:decl_name,Ptr{Uint8},(Ptr{Void},),d)
    ret = bytestring(s)
    c_free(s)
    ret
end

type CXXState
    callargs
    pvds
    templateresult
    isconstructor
    CXXState() = new()
end

const statemap = (Uint128=>CXXState)[]

state(uuid) = haskey(statemap,uuid) ? statemap[uuid] : (statemap[uuid] = CXXState())
function argexprs(state,argt,derefs,cpptype)
    if !isdefined(state,:callargs)
        state.callargs, state.pvds = buildargexprs(argt,derefs,cpptype)
    end
    @assert length(argt) == length(state.callargs) == length(state.pvds)
    state.callargs, state.pvds
end

get_pointee_name(t) = _decl_name(@cpp1 t->getPointeeCXXRecordDecl())
get_name(t) = _decl_name(@cpp1 t->getAsCXXRecordDecl())

function juliatype(t::pcpp"clang::Type")
    if @cpp1 t->isVoidType()
        return Void
    elseif @cpp1 t->isBooleanType()
        return Bool
    elseif @cpp1 t->isPointerType()
        return CppPtr{symbol(get_pointee_name(t)),()}
    elseif @cpp1 t->isReferenceType()
        t = pcpp"clang::Type"(ccall(:referenced_type,Ptr{Void},(Ptr{Void},),t.ptr))
        return CppRef{symbol(get_name(t)),()}
    elseif @cpp1 t->isIntegerType()
        if t == cpptype(Int64)
            return Int64
        elseif t == cpptype(Uint64)
            return Uint64
        elseif t == cpptype(Uint32)
            return Uint32
        elseif t == cpptype(Int32)
            return Int32
        end
        # This is wrong. Might be any int. Need to access the BuiltinType::Kind Enum
        return Int
    else
        rd = pcpp"clang::CXXRecordDecl"(@cpp1 t->getAsCXXRecordDecl())
        if rd.ptr != C_NULL
            return CppValue{symbol(get_name(t)),()}
        end
    end
    return Ptr{Void}
end

function cppret_constructor(argt,uuid,derefs,targs,f,thiscall,d,juliatype,cpptype)
    cxxd = tocxx(d)
    #@show cxxd
    if cxxd != C_NULL
        # Returned via sret
        return Void
        #t = typeForDecl(cxxd)
        #return CppValue{symbol(get_name(t))}
    else
        cxxt =  cxxtmplt(d)
        s = state(uuid)
        if cxxt != C_NULL
            ts = map(cpptype,targs)
            deduced_class = specialize_template(cxxt,targs,cpptype)
            @assert deduced_class != C_NULL
            s.templateresult = deduced_class
            s.isconstructor = true
            return Void
            #@cpp1 cxxt->findSpecialization(args,nargs,C_NULL)
        else
            # Just blatently assume it's a template call for now
            args = argexprs(s,argt,derefs,cpptype)[1]
            ts = pcpp"clang::Type"[cpptype(t) for t in targs]
            deduced_function = pcpp"clang::FunctionDecl"(ccall(:DeduceTemplateArguments,Ptr{Void},(Ptr{Void},Ptr{Void},Uint32,Ptr{Void},Uint32),d.ptr,ts,length(ts),args,length(args)))
            @assert deduced_function != C_NULL
            s.templateresult = deduced_function
            s.isconstructor = false
            t = pcpp"clang::Type"(ccall(:get_result_type,Ptr{Void},(Ptr{Void},),deduced_function.ptr))
            return juliatype(t)
        end
    end
end

cxxtmplt(x) = pcpp"clang::ClassTemplateDecl"(ccall(:cxxtmplt,Ptr{Void},(Ptr{Void},),x))

function cpp_ret_to_julia(argt,uuid,derefs,targs,f,thiscall)
    check_args(argt,f)
    d = pcpp"clang::Decl"(get_decl(argt,f,thiscall))
    t = pcpp"clang::Type"(ccall(:get_result_type,Ptr{Void},(Ptr{Void},),d.ptr))
    if t != C_NULL
        return juliatype(t)
    else
        return cppret_constructor(argt,uuid,derefs,targs,f,thiscall,d,juliatype,cpptype)
    end
end

tname{s,targs}(p::Type{CppPtr{s,targs}}) = s
tname{s,targs}(p::Type{CppValue{s,targs}}) = s
tname{s,targs}(p::Type{CppRef{s,targs}}) = s
cpptype{s}(p::Type{CppPtr{s,()}}) = pointerTo(typeForDecl(lookup_name2(string(tname(p)))))
cpptype{T<:CppPtr}(p::Type{CppPtr{T,()}}) = pointerTo(cpptype(T))
cpptype{s}(p::Type{CppValue{s,()}}) = typeForDecl(lookup_name2(string(tname(p))))
cpptype{s}(p::Type{CppRef{s,()}}) = referenceTo(typeForDecl(lookup_name2(string(tname(p)))))
cpptype{T}(p::Type{Ptr{T}}) = pointerTo(cpptype(T))

cpptype2(args...) = cpptype(args...)
cpptype2{s}(p::Type{CppPtr{s,()}}) = pointerTo(typeForDecl2(lookup_name2(string(tname(p)))))
cpptype2{T<:CppPtr}(p::Type{CppPtr{T,()}}) = pointerTo(cpptype2(T))
cpptype2{s}(p::Type{CppValue{s,()}}) = typeForDecl2(lookup_name2(string(tname(p))))
cpptype2{s}(p::Type{CppRef{s,()}}) = referenceTo(typeForDecl2(lookup_name2(string(tname(p)))))
cpptype2{T}(p::Type{Ptr{T}}) = pointerTo(cpptype2(T))

function cpptype{s,targs}(p::Type{CppValue{s,targs}})
    typeForDecl(specialize_template(pcpp"clang::ClassTemplateDecl"(lookup_name2(string(tname(p))).ptr),targs,cpptype))
end
function cpptype2{s,targs}(p::Type{CppValue{s,targs}})
    typeForDecl2(specialize_template(pcpp"clang::ClassTemplateDecl"(lookup_name2(string(tname(p))).ptr),targs,cpptype2))
end

get_decl2(args...) = pcpp"clang::Decl"(get_decl(args...))

const default_cgf = (pcpp"clang::CodeGen::CodeGenFunction")(ccall(:clang_get_cgf,Ptr{Void},()))

using Base.Meta

function specialize_template(cxxt::pcpp"clang::ClassTemplateDecl",targs,cpptype)
    ts = pcpp"clang::Type"[cpptype(t) for t in targs]
    d = pcpp"clang::ClassTemplateSpecializationDecl"(ccall(:SpecializeClass,Ptr{Void},(Ptr{Void},Ptr{Void},Uint32),cxxt.ptr,[p.ptr for p in ts],length(ts)))
    d
end

function build_cpp_call2(cexpr,member,ret,prefix,this,uuid)

    c = Expr(:call,:llvmcall,Expr(:tuple,member,uuid,derefs,targs,fname,this != nothing),
                             Expr(:tuple,ret,uuid,derefs,targs,fname,this != nothing),
                             Expr(:tuple,cpp_argt,uuid,fname,this != nothing))
    ret = c
    if this != nothing
        push!(c.args,this)
    else
        d = lookup_name2(fname)
        @assert d.ptr != C_NULL
        cxxd = tocxx(d)
        cxxt = cxxtmplt(d)
        if cxxd != C_NULL || cxxt != C_NULL
            T = CppValue{symbol(fname),tuple(targs...)}
            size =  cxxd != C_NULL ? sizeof(cxxd) : sizeof(pcpp"clang::CXXRecordDecl"(specialize_template(cxxt,targs,cpptype).ptr))
            ret = Expr(:block,
                :( r = ($(T))(Array(Uint8,$size)) ),
                c,
                :r)
            push!(c.args,:(convert(Ptr{Void},pointer(r.data))))
        end
    end

    return ret
end

createDerefExpr(e::pcpp"clang::Expr") = pcpp"clang::Expr"(ccall(:createDerefExpr,Ptr{Void},(Ptr{Void},),e.ptr))

function alloc(ty)
    @assert ty <: CppValue
    d = lookup_name2(string(tname(ty)))
    @assert d.ptr != C_NULL
    cxxd = tocxx(d)
    ty(Array(Uint8,sizeof(cxxd)))
end

import Base: getindex

function cppmember23_pre(f,argt,uuid,derefs,targs,func,thiscall,cpp_ret_to_julia,cpptype)
    llvmf = pcpp"llvm::Function"(f)
    s = state(uuid)
    d = isdefined(s,:templateresult) ? pcpp"clang::Decl"(s.templateresult.ptr) : get_decl2(argt,func,thiscall)
    @assert d.ptr != C_NULL
    cxxd = tocxx(d)
    args, types = process_args2(f,argt,builder,cpptype)
    rett = cpp_ret_to_julia(argt,uuid,derefs,targs,func,thiscall)
    rslot = pcpp"llvm::Value"(C_NULL)
    jlrslot = pcpp"llvm::Value"(C_NULL)
    if rett <: CppValue
        if isdefined(s,:isconstructor) && s.isconstructor
            # Via sret
            @assert s.templateresult.ptr == d.ptr
            @assert cxxd != C_NULL
            #t = typeForDecl(d)
            rslot = args[1]
        end
    end
    return (builder,llvmf,d,cxxd,s,args,types,rett,jlrslot,rslot)
end

function BuildMemberReference(base, t, IsArrow, name)
    pcpp"clang::Expr"(ccall(:BuildMemberReference,Ptr{Void},(Ptr{Void},Ptr{Void},Cint,Ptr{Uint8}),base, t, IsArrow, name))
end

function simple_decl_name(d)
    s = ccall(:simple_decl_name,Ptr{Uint8},(Ptr{Void},),d)
    ret = bytestring(s)
    c_free(s)
    ret
end

function cppmember23_post(builder,llvmf,d,cxxd,s,args,types,rett,jlrslot,rslot,argt,uuid,derefs,targs,func,thiscall,adjustargs,cpptype)
    if cxxd != C_NULL
        @assert argt[1] == Ptr{Void}
        #@show argt
        callargs, pvds = argexprs(s,argt[2:end],derefs,cpptype)
        associateargs(builder,argt[2:end],args[2:end],pvds)
        callargs = adjustargs(d,callargs)
        ctce = BuildCXXTypeConstructExpr(typeForDecl(cxxd),callargs)
        #dump(ctce)
        ccall(:emitexprtomem,Void,(Ptr{Void},Ptr{Void},Cint),ctce,args[1],1)
        ret = args[1]
    elseif thiscall
        ct = cpptype(argt[1])
        if argt[1] <: CppValue
            # We want the this argument to be modified in place,
            # so we make a deref of the pointer to the
            # data.
            pct = pointerTo(ct)
            pvd = CreateParmVarDecl(pct)
            AssociateValue(pvd,pct,args[1])
            dre = createDerefExpr(CreateDeclRefExpr(pvd,pct))
        else
            pvd = CreateParmVarDecl(ct)
            AssociateValue(pvd,ct,args[1])
            dre = CreateDeclRefExpr(pvd,ct)
        end
        me = BuildMemberReference(dre, ct, argt[1] <: CppPtr, simple_decl_name(d))
        #me = CreateUnresolvedMemberExpr(false, dre, ct, argt[1] <: CppPtr, get_name(d))
        #me = CreateMemberExpr(dre,argt[1] <: CppPtr,tovdecl(d))

        callargs, pvds = argexprs(s,argt[2:end],derefs,cpptype)
        associateargs(builder,argt[2:end],args[2:end],pvds)
        callargs = adjustargs(d,callargs)
        mce = BuildCallToMemberFunction(me,callargs)

        ret = pcpp"llvm::Value"(ccall(:emitcppmembercallexpr,Ptr{Void},(Ptr{Void},Ptr{Void}),mce.ptr,rslot))
    else
        callargs, pvds = argexprs(s,argt,derefs,cpptype)
        associateargs(builder,argt,args,pvds)
        callargs = adjustargs(d,callargs)
        ret::pcpp"llvm::Value" = pcpp"llvm::Value"(ccall(:emit_cpp_call,Ptr{Void},(Ptr{Void},Ptr{Void},Ptr{Ptr{Void}},Ptr{Void},Csize_t,Bool,Bool,Ptr{Void}),
            d,args,types,callargs,length(args),false,false,rslot))
    end
    if jlrslot != C_NULL && rslot != C_NULL && (length(args) == 0 || rslot != args[1])
        @cpp1 builder->CreateRet(pcpp"llvm::Value"(jlrslot.ptr))
    elseif ret == C_NULL
        @cpp1 builder->CreateRetVoid()
    else
        #@show rett
        if rett == Void
            @cpp1 builder->CreateRetVoid()
        else
            if rett <: CppPtr || rett <: CppRef
                llvmt = pcpp"llvm::Type"(@cpp1 llvmf->getReturnType())
                undef = pcpp"llvm::Value"(@cpp1 llvm::UndefValue::get(llvmt))
                elty = pcpp"llvm::Type"(@cpp1 llvmt->getStructElementType(uint32(0)))
                ret = pcpp"llvm::Value"(@cpp1 builder->CreateBitCast(ret,elty))
                ret = pcpp"llvm::Value"(ccall(:create_insert_value,Ptr{Void},(Ptr{Void},Ptr{Void},Csize_t),undef.ptr,ret.ptr,0))
            end
            @cpp1 builder->CreateRet(ret)
        end
    end
end

adjustargs_dummy(d,callargs) = callargs

getindex(p::pcpp"clang::FucntionDecl",i) = pcpp"ParamVarDecl"(@cpp1 p->getParamDecl(i))
function cpp_member2(f,mod,argt,uuid,derefs,targs,func,thiscall)
    check_args(argt,f)
    initialize(mod,f) do
        (builder,llvmf,d,cxxd,s,args,types,rett,jlrslot,rslot) = cppmember23_pre(f,argt,uuid,derefs,targs,func,thiscall,cpp_ret_to_julia,cpptype)
        cppmember23_post(builder,llvmf,d,cxxd,s,args,types,rett,jlrslot,rslot,argt,uuid,derefs,targs,func,thiscall,adjustargs_dummy,cpptype)
    end
    delete!(statemap,uuid)
end

function cpp_assignment(f,mod,argt,path)
    t = argt[1]
    @assert t <: CppRef
    ct = lookup_name2(string(t.parameters[1]))

end

chartype() = pcpp"clang::Type"(unsafe_load(cglobal(:cT_cchar,Ptr{Void})))
cpptype(::Type{Uint8}) = chartype()#pcpp"clang::Type"(unsafe_load(cglobal(:cT_uint8,Ptr{Void})))
cpptype(::Type{Int8}) = pcpp"clang::Type"(unsafe_load(cglobal(:cT_int8,Ptr{Void})))
cpptype(::Type{Uint32}) = pcpp"clang::Type"(unsafe_load(cglobal(:cT_uint32,Ptr{Void})))
cpptype(::Type{Int32}) = pcpp"clang::Type"(unsafe_load(cglobal(:cT_int32,Ptr{Void})))
cpptype(::Type{Uint64}) = pcpp"clang::Type"(unsafe_load(cglobal(:cT_uint64,Ptr{Void})))
cpptype(::Type{Int64}) = pcpp"clang::Type"(unsafe_load(cglobal(:cT_int64,Ptr{Void})))
cpptype(::Type{Bool}) = pcpp"clang::Type"(unsafe_load(cglobal(:cT_int1,Ptr{Void})))
cpptype(::Type{Void}) = pcpp"clang::Type"(unsafe_load(cglobal(:cT_void,Ptr{Void})))

macro cpp2(expr)
    cpps_impl(expr,cpp_member2,build_cpp_call2,cpp_ret_to_julia)
end

typeForDecl2(p::pcpp"clang::TypeDecl") = @cpp2 p->getTypeForDecl()
function typeForDecl2(p::pcpp"clang::Decl")
    d::pcpp"clang::Decl" = p
    td = @cpp2 llvm::dyn_cast_or_null{vcpp"clang::TypeDecl"}(d)
    typeForDecl2(td)
end

llvmconst(builder,::Type{Any},x::ANY) =
    (@cpp2 builder->CreateIntToPtr((@cpp2 llvm::ConstantInt::get(llvmty(Uint64),convert(Uint64,pointer_from_objref(x)::Ptr{Void}))), llvmty(Ptr{Void})))
llvmconst{T}(builder,::Type{T},x) = @cpp2 llvm::ConstantInt::get(llvmty(T),convert(Uint64,x))

const llvmc = rcpp"llvm::LLVMContext"(ccall(:jl_get_llvmc,Ptr{Void},()))

llvmty(::Type{Any}) = pcpp"llvm::Type"(unsafe_load(cglobal(:jl_pvalue_llvmt,Ptr{Void})))
llvmty(::Type{Ptr{Void}}) = pcpp"llvm::Type"((@cpp2 (@cpp2 llvm::Type::getInt8Ty(llvmc))->getPointerTo()).ptr)
llvmty(::Type{Uint64}) = pcpp"llvm::Type"((@cpp2 llvm::Type::getInt64Ty(llvmc)).ptr)
llvmty(::Type{Uint32}) = pcpp"llvm::Type"((@cpp2 llvm::Type::getInt32Ty(llvmc)).ptr)

function cpp_ref_ret(retargs...)
    @show retargs
end

function cpp_ref_ref(refargs...)
    @show refargs
end

function adjustEnum(d)

end

const CK_Dependent      = 0
const CK_BitCast        = 1
const CK_LValueBitCast  = 2
const CK_LValueToRValue = 3
const CK_NoOp           = 4
const CK_BaseToDerived  = 5
const CK_DerivedToBase  = 6
const CK_UncheckedDerivedToBase = 7
const CK_Dynamic = 8
const CK_ToUnion = 9
const CK_ArrayToPointerDecay = 10
const CK_FunctionToPointerDecay = 11
const CK_NullToPointer = 12
const CK_NullToMemberPointer = 13
const CK_BaseToDerivedMemberPointer = 14
const CK_DerivedToBaseMemberPointer = 15
const CK_MemberPointerToBoolean = 16
const CK_ReinterpretMemberPointer = 17
const CK_UserDefinedConversion = 18
const CK_ConstructorConversion = 19
const CK_IntegralToPointer = 20
const CK_PointerToIntegral = 21
const CK_PointerToBoolean = 22
const CK_ToVoid = 23
const CK_VectorSplat = 24
const CK_IntegralCast = 25

function createCast(arg,t,kind)
    pcpp"clang::Expr"(ccall(:createCast,Ptr{Void},(Ptr{Void},Ptr{Void},Cint),arg,t,kind))
end

function adjustargs3(d,callargs)
    if @cpp2 llvm::isa{vcpp"clang::FunctionDecl"}(d)
        fdecl = @cpp2 llvm::dyn_cast_or_null{vcpp"clang::FunctionDecl"}(d)
        @assert fdecl != C_NULL
        for i = 0:((@cpp2 fdecl->getNumParams())-1)
            param = @cpp2 fdecl->getParamDecl(i)
            t = pcpp"clang::Type"(ccall(:getOriginalTypePtr,Ptr{Void},(Ptr{Void},),param))
            if @cpp2 t->isEnumeralType()
                callargs[i+1] = createCast(callargs[i+1],t,CK_IntegralCast)
            end
        end
    end
    callargs
end

function cpp_member3(f,mod,argt,uuid,derefs,targs,func,thiscall)
    check_args(argt,f)
    initialize(mod,f) do
        (builder,llvmf,d,cxxd,s,args,types,rett,jlrslot,rslot) = cppmember23_pre(f,argt,uuid,derefs,targs,func,thiscall,cpp_ret_to_julia,cpptype)
        adjustEnum(d)
        cppmember23_post(builder,llvmf,d,cxxd,s,args,types,rett,jlrslot,rslot,argt,uuid,derefs,targs,func,thiscall,adjustargs3,cpptype)
    end
    delete!(statemap,uuid)
end

macro cpp3(expr)
    cpps_impl(expr,cpp_member3,build_cpp_call2,cpp_ret_to_julia)
end

function juliatype4(t::pcpp"clang::Type")
    if @cpp1 t->isVoidType()
        return Void
    elseif @cpp1 t->isBooleanType()
        return Bool
    elseif @cpp1 t->isPointerType()
        cxxd = @cpp3 t->getPointeeCXXRecordDecl()
        if cxxd != C_NULL
            return CppPtr{symbol(get_pointee_name(t)),()}
        else
            pt = pcpp"clang::Type"(ccall(:referenced_type,Ptr{Void},(Ptr{Void},),t.ptr))
            tt = juliatype4(pt)
            if tt <: CppPtr
                return CppPtr{tt,()}
            else
                return Ptr{tt}
            end
        end
    elseif @cpp1 t->isReferenceType()
        t = pcpp"clang::Type"(ccall(:referenced_type,Ptr{Void},(Ptr{Void},),t.ptr))
        return CppRef{symbol(get_name(t)),()}
    elseif @cpp1 t->isCharType()
        return Uint8
    elseif @cpp1 t->isIntegerType()
        if t == cpptype(Int64)
            return Int64
        elseif t == cpptype(Uint64)
            return Uint64
        elseif t == cpptype(Uint32)
            return Uint32
        elseif t == cpptype(Int32)
            return Int32
        elseif t == cpptype(Uint8) || t == chartype()
            return Uint8
        elseif t == cpptype(Int8)
            return Int8
        end
        # This is wrong. Might be any int. Need to access the BuiltinType::Kind Enum
        return Int
    else
        rd = pcpp"clang::CXXRecordDecl"(@cpp1 t->getAsCXXRecordDecl())
        if rd.ptr != C_NULL
            targt = ()
            if @cpp2 llvm::isa{vcpp"clang::ClassTemplateSpecializationDecl"}(pcpp"clang::Decl"(rd.ptr))
                tmplt = @cpp2 llvm::dyn_cast_or_null{vcpp"clang::ClassTemplateSpecializationDecl"}(pcpp"clang::Decl"(rd.ptr))
                targs = @cpp2 tmplt->getTemplateArgs()
                args = pcpp"clang::Type"[]
                for i = 0:((@cpp2 targs->size())-1)
                    targ = @cpp2 targs->get(uint32(i))
                    push!(args,pcpp"clang::Type"(ccall(:getTargType,Ptr{Void},(Ptr{Void},),targ)))
                end
                targt = tuple(map(juliatype4,args)...)
            end
            return CppValue{symbol(get_name(t)),targt}
        end
    end
    return Ptr{Void}
end

function cpp_ret_to_julia4(argt,uuid,derefs,targs,f,thiscall)
    check_args(argt,f)
    d = pcpp"clang::Decl"(get_decl(argt,f,thiscall))
    t = pcpp"clang::Type"(ccall(:get_result_type,Ptr{Void},(Ptr{Void},),d.ptr))
    if t != C_NULL
        return juliatype4(t)
    else
        return cppret_constructor(argt,uuid,derefs,targs,f,thiscall,d,juliatype4,cpptype2)
    end
end

function toLLVM(p::pcpp"clang::Type")
    #cgt = default_compiler.cgt
    cgt = (pcpp"clang::CodeGen::CodeGenTypes")(ccall(:clang_get_cgt,Ptr{Void},()))
    @cpp2 cgt->ConvertType(@cpp2 clang::QualType(p,uint32(0)))
end

function cpp_member4(f,mod,argt,uuid,derefs,targs,func,thiscall)
    check_args(argt,f)
    initialize(mod,f) do
        (builder,llvmf,d,cxxd,s,args,types,rett,jlrslot,rslot) = cppmember23_pre(f,argt,uuid,derefs,targs,func,thiscall,cpp_ret_to_julia4,cpptype2)
        if rett !== Void && rett <: CppValue && (!isdefined(s,:isconstructor) || !s.isconstructor)
            t = pcpp"clang::Type"(ccall(:get_result_type,Ptr{Void},(Ptr{Void},),d.ptr))
            @assert t != C_NULL
            cxxt = pcpp"clang::CXXRecordDecl"(@cpp1 t->getAsCXXRecordDecl())
            jlrslot, rslot = createAlloc(builder,cxxt,rett)
            rslot = @cpp2 builder->CreateBitCast(rslot, getPointerTo(toLLVM(t)))
        end
        cppmember23_post(builder,llvmf,d,cxxd,s,args,types,rett,jlrslot,rslot,argt,uuid,derefs,targs,func,thiscall,adjustargs3,cpptype2)
    end
end

function createAlloc(builder::pcpp"clang::CodeGen::CGBuilderTy",cxxt::pcpp"clang::CXXRecordDecl",rett)
    # Prepare struct return
    # We emit:
    #
    # arr = jl_alloc_array_1d(Uint8,sizeof(cxxt))
    # obj = allocobj(2)
    # obj[0] = rett
    # obj[1] = arr
    #
    # First set up the function objects
    mod = pcpp"llvm::Module"(unsafe_load(cglobal(:clang_shadow_module,Ptr{Void})))
    args = pcpp"llvm::Type"[llvmty(Ptr{Void}),llvmty(Csize_t)]
    nargs::Csize_t = convert(Csize_t,length(args))
    aftype1 = @cpp2 llvm::FunctionType::get(llvmty(Ptr{Void}),
        (@cpp2 llvm::ArrayRef{pcpp"llvm::Type"}(CppPtr{pcpp"llvm::Type",()}(pointer(args)),nargs)),
        false)
    jl_alloc_array = @cpp3 llvm::Function::Create(aftype1, 0,
        pointer("jl_alloc_array_1d"), mod)

    args = pcpp"llvm::Type"[llvmty(Csize_t)]
    nargs = convert(Csize_t,length(args))
    aftype2 = @cpp2 llvm::FunctionType::get(llvmty(Any),
        (@cpp2 llvm::ArrayRef{pcpp"llvm::Type"}(CppPtr{pcpp"llvm::Type",()}(pointer(args)),nargs)),
        false)
    allocobj = @cpp3 llvm::Function::Create(aftype2, 0,
                               pointer("allocobj"), mod)

    # Emit the actual allocation
    puint8 = llvmconst(builder,Any,Array{Uint8,1})
    size = llvmconst(builder,Csize_t,sizeof(cxxt))
    arr = @cpp2 builder::pcpp"clang::CodeGen::CGBuilderTy"->CreateCall2(jl_alloc_array,puint8,size)
    obj = @cpp3 builder::pcpp"clang::CodeGen::CGBuilderTy"->CreateCall(allocobj,llvmconst(builder,Csize_t,2))
    obj2 = @cpp2 builder->CreateBitCast(obj,getPointerTo(llvmty(Ptr{Void})))
    c = llvmconst(builder,Any,rett)
    @cpp2 builder::pcpp"clang::CodeGen::CGBuilderTy"->CreateStore(c,obj2)
    gep = CreateConstGEP1_32(builder,obj2,1)::pcpp"llvm::Value"
    @cpp2 builder::pcpp"clang::CodeGen::CGBuilderTy"->CreateStore(arr,gep)
    arrb = @cpp2 builder->CreateBitCast(arr,getPointerTo(@cpp2 arr->getType()))
    arr2 = @cpp2 builder->CreateLoad(CreateConstGEP1_32(builder,arrb,1))
    obj, arr2
end

macro cpp(expr)
    cpps_impl(expr,cpp_member4,build_cpp_call2,cpp_ret_to_julia4)
end

function ClangCompiler()
    #instance = @cpp2new "clang::CompilerInstance"
    #@cpp2 instance->createDiagnostics()
    #lopts = @cpp2 instance->getLangOpts()
    ##@cpp2 lopts.CPlusPlus = 1
    #@cpp2 lopts.LineComment = 1
    #@cpp2 lopts.Bool = 1
    ##@cpp2 lopts.WChar = 1
    #@cpp2 lopts.C99 = 1
    #@cpp2 lopts.ImplicitInt = 0
    #ClangCompiler(instance)
    #@cpp2 to->Triple = @cpp2 llvm::sys::getProcessTriple();
    #tin = @cpp2 clang::TargetInfo::CreateTargetInfo(@cpp2 instance->getDiagnostics(), to)
    #@cpp2 instance->setTarget(tin)
    #@cpp2 instance->createFileManager()
    #@cpp2 instance->createSourceManager(@cpp2 instance->getFileManager())
    #@cpp2 instance->createPreprocessor()
    #@cpp2 instance->createASTContext()
    #@cpp2 instance->setASTConsumer(@cpp2new JuliaCodeGenerator)
    #@cpp2 instance->createSema(@cpp2 clang::TU_Prefix,C_NULL)
    #TD = @cpp2new DataLayout(@cpp2 tin->getTargetDescription())
    #cgm = @cpp2new "clang::CodeGen::CodeGenModule"(
    #    @cpp2 instance->getASTContext(),
    #    @cpp2 instance->getCodeGenOpts(),
    #    ccall(:jl_get_llvm_module,Ptr{Void},()),
    #    *TD,
    #    @cpp2 instance->getDiagnostics()
    #)
    #cgt = @cpp2new "clang::CodeGen::CodeGenTypes"(*cgm)
    #cgf = @cpp2new "clang::CodeGen::CodeGenFunction"(*cgm)
    #new(handle,cgt,cgm)
end

function foo(instance::pcpp"clang::CompilerInstance")
    #instance = @cpp2new "clang::CompilerInstance"
    @cpp2 instance->createDiagnostics()
    @cpp2 instance->getLangOpts()
end

#code_llvm(foo,(pcpp"clang::CompilerInstance",))

#f(ee) = @cpp2 ee->getDataLayout()
#code_llvm(f,(pcpp"llvm::ExecutionEngine",))

#code_llvm(sizeof,(pcpp"clang::CXXRecordDecl",))
#println(sizeof(lookup_name2("clang::CompilerInstance")))

bar1(r::rcpp"llvm::LLVMContext") = @cpp2 llvm::Type::getInt8Ty(r)
#code_llvm(bar1,(rcpp"llvm::LLVMContext",))
#@show bar1(llvmc)

storageFor(d::pcpp"clang::Decl") = Array(Uint8,sizeof(d))

function bar()
    t = typeForDecl(lookup_name2("clang::QualType"))
    pt = pointerTo(t)
    #@show llvmc
    llvmt = bar1(llvmc)
    @show llvmt
    #dump(llvmt)
    pllvmt = @cpp2 llvm::PointerType::get(llvmt,uint32(0))
    expr = createDeref(pt,@cpp2 llvm::UndefValue::get(pllvmt))
    @cpp2 expr->dump()
    expr
end

#ct = pointerTo(typeForDecl(lookup_name2("llvm::ExecutionEngine")))
#pvd = CreateParmVarDecl(ct)
#dump(pvd)
#dre = CreateDeclRefExpr(pvd,ct)
#dump(dre)
#me = CreateMemberExpr(dre,true,tovdecl(lookup_name2("llvm::ExecutionEngine::getDataLayout")))
#dump(me)
#mce = BuildCallToMemberFunction(me,pcpp"clang::Expr"[])
#dump(mce)

#baz() = @cpp2 clang::SourceLocation()
#code_llvm(baz,())

#dump(cpptype(Uint32))

#code_llvm(toLLVM,(pcpp"clang::Type",))

#toLLVM(typeForDecl(lookup_name2("llvm::ExecutionEngine")))

# THE GOAL FOR TODAY
# Create a graph from a function

function baz()
    graphf = code_llvmf(toLLVM,(pcpp"clang::Type",))
    @cpp2 llvm::ViewGraph(graphf,@cpp2 llvm::Twine())
end

#code_lowered(baz,())
#code_llvm(baz,())

dump(x::CppRef) = @cpp2 x->dump()

sstring = pcpp"clang::ClassTemplateSpecializationDecl"(lookup_name2("std::string").ptr)

get_targs(x) = @cpp2 x->getTemplateArgs()
getindex(targ::rcpp"clang::TemplateArgumentList",i) = @cpp2 targ->get(uint32(i))

function astype(targ::rcpp"clang::TemplateArgument")
    t = @cpp2 targ->getAsType()
    @cpp2 t->getTypePtr()
end

#dump(astype(get_targs(sstring)[0]))

#code_llvm(baz2,())
#code_llvmf(baz2,())
sdata(v) = @cpp v->data()
ssize(v) = @cpp v->size()
test() = @cpp std::string(pointer("test"),4)

using Cairo
using GraphViz

#Graph(baz2(toLLVM,(pcpp"clang::Type",))) |> display

#println("SUCCESS")

# Debugger
initd() = @cpp lldb_private::Debugger::Initialize(cast(C_NULL,vcpp"lldb_private::Debugger::LoadPluginCallbackType"))
initd()

gc_disable()

debugger() = @cpp vcpp"lldb::DebuggerSP"((@cpp lldb_private::Debugger::CreateInstance()).data)->get()
const dbg = debugger()
ci(dbg::pcpp"lldb_private::Debugger") = @cpp dbg->GetCommandInterpreter()
function hc(ci::rcpp"lldb_private::CommandInterpreter",cmd)
    cro = @cpp lldb_private::CommandReturnObject()
    @cpp ci->HandleCommand(pointer(cmd),0,cro)
    @cpp cro->GetOutputData()
end

stdout = pcpp"FILE"(ccall(:fdopen,Ptr{Void},(Int32,Ptr{Uint8}),1,"a"))

function setSTDOUT(dbg,stdout)
    @cpp dbg->SetOutputFileHandle(stdout,false)
end

function setSTDERR(dbg,stdout)
    @cpp dbg->SetErrorFileHandle(stdout,false)
end

setSTDOUT(dbg,stdout)
setSTDERR(dbg,stdout)

#Graph(baz2()) |> display

#bar(cgt,d) = @cpp2 cgt->ConvertRecordDeclType(d)
#t = bar(default_compiler.cgt,lookup_name2("clang::CompilerInstance"))
#function baz(cgt)
#   dl = @cpp2 cgt->getDataLayout()
#end
#dl = baz(default_compiler.cgt)
#test(dl,t) = @cpp2 dl->getTypeSizeInBits(t)
#test(dl,t)
#test(dl,t)
#println(foo())

ext: jl


text:
using CSV
using DataFrames
using LinearAlgebra

println("a")

fdata = ARGS[1]
ftime = ARGS[2]
dir = ARGS[3]
tfnum = parse(Int32,ARGS[4])
pnum = parse(Int32,ARGS[5])
cnum = parse(Int32,ARGS[6])
maxite = parse(Int32,ARGS[7])

maxB = 2.0
minB = -10.0

if !(isdir(dir))
    mkdir(dir)
end

#X = readtable(fdata, separator='\t', header=false)[1:tfnum,1:cnum]
X = CSV.read(fdata, header=false)
X = convert(Matrix, X)
W = zeros(tfnum, pnum)
Z = zeros(pnum, cnum)
WZ = zeros(tfnum, cnum)

pseudotime = CSV.read(ftime, header=false)[1:cnum,2]
pseudotime = pseudotime/maximum(pseudotime)

new_B = zeros(pnum)
old_B = zeros(pnum)

#initialization
RSS = Inf
for i in 1:pnum
    new_B[i] = rand()*(maxB-minB)+minB
    old_B[i] = new_B[i]
end

function sample_Z()
    for i in 1:pnum
        for j in 1:cnum
            Z[i,j] = exp(new_B[i]*pseudotime[j]) + (rand()*0.002-0.001)
        end
    end
end

#optimization
for ite in 1:maxite
    #sampling B
    target = rand(1:pnum)
    global new_B[target] = rand()*(maxB-minB)+minB

    #for last calc
    if ite==maxite
        global new_B = copy(old_B)
    end

    #sample Z from new B
    sample_Z()

    #regression
    for i in 1:tfnum
        W[i,:] = (Z * Z')^-1 * (Z * X[i,:])
    end
    #prediction
    WZ = W * Z

    #RSS
    tmp_RSS = 0
    for i in 1:tfnum
        tmp_RSS += sum((X[i,:]-WZ[i,:]).*(X[i,:]-WZ[i,:]))
    end

    if tmp_RSS < RSS
        global RSS = tmp_RSS
        global old_B[target] = new_B[target]
    else
        global new_B[target] = old_B[target]
    end
end

#output RSS
open(dir*"/RSS.txt", "w") do f
    println(f, RSS)
end

#output W
#writetable(dir*"/W.txt", convert(DataFrame, W), separator = '\t', header = false)
DataFrame(W) |> CSV.write(dir*"/W.txt", delim='\t', writeheader=false)

#infer A
B = zeros(pnum, pnum)
for i in 1:pnum
    B[i,i] = new_B[i]
end
invW = pinv(W)
A = W * B * invW

#write A and B
#writetable(dir*"/A.txt", convert(DataFrame, A), separator = '\t', header = false)
#writetable(dir*"/B.txt", convert(DataFrame, B), separator = '\t', header = false)
DataFrame(A) |> CSV.write(dir*"/A.txt", delim='\t', writeheader=false)
DataFrame(B) |> CSV.write(dir*"/B.txt", delim='\t', writeheader=false)


text:
#Demo 10: Mixed-signal "Simulation"
#-------------------------------------------------------------------------------
using InspectDR
using Colors

#==Input
===============================================================================#

#Constants
#-------------------------------------------------------------------------------
black = RGB24(0, 0, 0)
white = RGB24(1, 1, 1)
red = RGB24(1, 0, 0)
green = RGB24(0, 1, 0)
blue = RGB24(0, 0, 1)

line_default = line(color=blue, width=3)
line_overlay = line(color=red, width=3)

#Input data
#-------------------------------------------------------------------------------
ppcycle = 40 #Points per cycle (divisible by 4 to get notches)
T=1/60 #Clock period
ncycles = 15
VSUPPLY = 120 #VRMS - mains
VREG = 80 #Rectifier voltage
τ = T/2 #Time constant for discharge (output load)

#=="Simulation" (really rudimentary to keep things simple)
===============================================================================#
mains_fn(t) = VSUPPLY*sqrt(2)*cos.(2pi*(t./T))
#Find time where mains crosses given threshold:
mains_xfn(thresh) = acos(thresh/(VSUPPLY*sqrt(2)))*(T/(2pi))

function update_discharge(vreg, Δt, i)
	if 0==vreg[i] #On discharge cycle:
		vreg[i] = VREG*exp(-Δt/τ)
	end
end

#Compute info for a single cycle:
#-------------------------------------------------------------------------------
t = collect(range(0, stop=T, length=ppcycle+1)[1:end-1]) #Time
npts = length(t); hpts = div(npts, 2) #npts should match ppcycle

mains = mains_fn(t)
mains_rect = abs.(mains)
regactive = mains_rect .> VREG
regactive_sig = regactive * 1.0 #Not bool
vreg = regactive_sig*VREG #Voltage @ rectifier - not yet filtered
#Emulate filtering/discharge:
	#Really dumb algorithm:
	d_start = mains_xfn(VREG) #Start of 1st discharge cycle
#	d_end = d_start+2*(T/4-d_start)
	for i in 1:hpts
		update_discharge(vreg, t[i]-d_start, i)
	end
	d_start += T/2 #Next discharge cycle
	for i in hpts:npts
		update_discharge(vreg, t[i]-d_start, i)
	end

#Extend data for multiple cycles:
#-------------------------------------------------------------------------------
t = collect(range(0, stop=ncycles*T, length=ncycles*ppcycle+1)[1:end-1]) #Time
mains = repeat(mains, outer=ncycles)
mains_rect = repeat(mains_rect, outer=ncycles)
vreg = repeat(vreg, outer=ncycles)
regactive_sig = repeat(regactive_sig, outer=ncycles)

gaterange_list = [4.6:6.1, 9.4:12.9] #In # of cycles
gate_b = 1.0 .+ zero(t) #Active low
for gaterange in gaterange_list
	irange = ppcycle*gaterange
	istart = round(Int, minimum(irange))
	istop = round(Int, maximum(irange))
	gate_b[istart:istop] .= 0
end
vrect_gated = vreg.*gate_b
vrect_thresh = zero(t) .+ VREG

#==Generate plot
===============================================================================#
mplot = InspectDR.Multiplot(title="Mixed-Signal \"Simulation\": $(VREG)V Regulator")
nstrips = 4

plot = add(mplot, InspectDR.transientplot([:lin for i in 1:nstrips],
	title="", #No title - use strip labels instead
	ylabels=["Potential (V)" for i in 1:nstrips]
))
plot.layout[:enable_legend] = true
plot.layout[:halloc_legend] = 150
ystriplabels = plot.annotation.ystriplabels #Shortcut
yext_digital = InspectDR.PExtents1D(-0.2, 1.2) #"Digital" signals (pad)
yext_output = InspectDR.PExtents1D(0,1.2*VREG) #Ouptut: pad above

#Add waveforms & overlay useful signals:
push!(ystriplabels, "Regulator Output")
plot.strips[1].yext_full = yext_output
	wfrm = add(plot, t, vreg, id="regulated", strip=1); wfrm.line = line_default
	#Overlay gated output onto internal:
	wfrm = add(plot, t, vrect_gated, id="output (gated)", strip=1); wfrm.line = line_overlay

push!(ystriplabels, "Digital Sensor Output & Control Signals")
plot.strips[2].yext_full = yext_digital
	wfrm = add(plot, t, regactive_sig, id="|mains|>thresh", strip=2); wfrm.line = line_default
	#Overlay gate onto gated output:
	wfrm = add(plot, t, gate_b, id="gate_b", strip=2); wfrm.line = line_overlay

push!(ystriplabels, "Rectifier Ouptut")
	wfrm = add(plot, t, mains_rect, id="|mains|", strip=3); wfrm.line = line_default
	#Overlay threshold onto |mains|, for convenience
	wfrm = add(plot, t, vrect_thresh, id="thresh", strip=3); wfrm.line = line_overlay

push!(ystriplabels, "Input: Mains")
	wfrm = add(plot, t, mains, id="mains", strip=4); wfrm.line = line_default

gplot = display(InspectDR.GtkDisplay(), mplot)

mplot.layout[:halloc_plot] = 700; mplot.layout[:valloc_plot] = 600
InspectDR.write_png("export_rectifier.png", mplot)

:DONE


text:
### A Pluto.jl notebook ###
# v0.12.21

using Markdown
using InteractiveUtils

# ╔═╡ ce85691e-fc77-11ea-0aee-df1ba52cecd7
using Pkg, DrWatson

# ╔═╡ ce85a7b2-fc77-11ea-2bec-0d7f0af3c59b
begin
	#@quickactivate "StatisticalRethinkingStan"
	using StanQuap
	using StatisticalRethinking
end

# ╔═╡ 9741f266-fc76-11ea-0689-cd04b7b16135
md"## Clip-05-10-11s.jl"

# ╔═╡ b9c3f450-fcdb-11ea-2ca9-27f5dbb73a98
md"##### The model m5.3s represents a regression of Divorce on both Marriage rate and MedianAgeMarriage and is defined as:"

# ╔═╡ aba487fe-fcdb-11ea-25e1-67c6f9596a16
md"
```
model {
  vector[N] mu;               // mu is a vector
  a ~ normal(0, 0.2);         // Priors
  bA ~ normal(0, 0.5);
  bM ~ normal(0, 0.5);
  sigma ~ exponential(1);
  mu = a + bA * A + bM * M;
  D ~ normal(mu , sigma);     // Likelihood
}
```
"

# ╔═╡ c62fab76-fcdb-11ea-1b75-e71f5c77eccf
md"##### D (Divorce rate), M (Marriage rate) and A (MediumAgeMarriage) are all standardized."

# ╔═╡ ce861b20-fc77-11ea-0b79-8dd922420260
begin
	df = CSV.read(sr_datadir("WaffleDivorce.csv"), DataFrame);
	scale!(df, [:Marriage, :MedianAgeMarriage, :Divorce])
	data = (N=size(df, 1), D=df.Divorce_s, A=df.MedianAgeMarriage_s,
		M=df.Marriage_s)
end

# ╔═╡ ce8df1ec-fc77-11ea-1c7d-ad620c19fbbd
stan5_1 = "
	data {
	 int N; // Sample size
	 vector[N] D; // Outcome
	 vector[N] A; // Predictor
	}

	parameters {
	 real a; // Intercept
	 real bA; // Slope (regression coefficients)
	 real < lower = 0 > sigma;    // Error SD
	}

	model {
	  vector[N] mu;               // mu is a vector
	  a ~ normal(0, 0.2);         // Priors
	  bA ~ normal(0, 0.5);
	  sigma ~ exponential(1);
	  mu = a + bA * A;
	  D ~ normal(mu , sigma);   // Likelihood
	}
";

# ╔═╡ 8a7c3e5c-8116-11eb-1911-b940b352dfc9
md"##### Result rethinking:"

# ╔═╡ 8a7c7662-8116-11eb-3540-1dea64d7eec6
rethinking = "
	   mean   sd  5.5% 94.5%
a      0.00 0.10 -0.16  0.16
bA    -0.57 0.11 -0.74 -0.39
sigma  0.79 0.08  0.66  0.91
";

# ╔═╡ 8a7d461c-8116-11eb-043e-ffb6bf7d0737
begin
	data = (N = size(df, 1), D = df.Divorce_s, A = df.MedianAgeMarriage_s,
		M = df.Marriage_s)
	init = (a=1.0, bA=1.0, bM=1.0, sigma=10.0)
	q5_1s, m5_1s, om5_1s = stan_quap("m5.1s", stan5_1; data, init)
	if !isnothing(q5_1s)
		quap5_1s_df = sample(q5_1s)
		PRECIS(quap5_1s_df)
	end
end

# ╔═╡ b7c70164-8116-11eb-0f7e-754d76c430ec
stan5_2 = "
data {
  int N;
  vector[N] D;
  vector[N] M;
}
parameters {
  real a;
  real bM;
  real<lower=0> sigma;
}
model {
  vector[N] mu = a + bM * M;
  a ~ normal( 0 , 0.2 );
  bM ~ normal( 0 , 0.5 );
  sigma ~ exponential( 1 );
  D ~ normal( mu , sigma );
}
";

# ╔═╡ bdc8090a-8116-11eb-07c8-d343006e2d79
begin
	q5_2s, m5_2s, om = stan_quap("m5.2s", stan5_2; data)
	if !isnothing(q5_2s)
		quap5_2s_df = sample(q5_2s)
	end
	PRECIS(quap5_2s_df)
end

# ╔═╡ ce79d490-8116-11eb-0f1d-8d1719cf1e5b
stan5_3 = "
data {
  int N;
  vector[N] D;
  vector[N] M;
  vector[N] A;
}
parameters {
  real a;
  real bA;
  real bM;
  real<lower=0> sigma;
}
model {
  vector[N] mu = a + + bA * A + bM * M;
  a ~ normal( 0 , 0.2 );
  bA ~ normal( 0 , 0.5 );
  bM ~ normal( 0 , 0.5 );
  sigma ~ exponential( 1 );
  D ~ normal( mu , sigma );
}
";

# ╔═╡ e4731414-8116-11eb-1340-0f2a41529348
begin
	q5_3s, m5_3s, om5_3 = stan_quap("m5.3s", stan5_3; data, init)
	if !isnothing(q5_3s)
		quap5_3s_df = sample(q5_3s)
	end
	PRECIS(quap5_3s_df)
end

# ╔═╡ 47566d90-fcda-11ea-0662-8fa0dd0583c4
md"##### Normal estimates:"

# ╔═╡ ce8e68c0-fc77-11ea-30eb-3fcbdd292d2c
if !isnothing(m5_1s) &&!isnothing(m5_2s) && !isnothing(m5_3s)
	(s1, p1) = plot_model_coef([m5_1s, m5_2s, m5_3s], [:bA, :bM];
		title="Particles (Normal) estimates")
	p1
end

# ╔═╡ cea73280-fc77-11ea-348f-8973a4e7a5d3
md"## End of clip-05-10-11s.jl"

# ╔═╡ Cell order:
# ╟─9741f266-fc76-11ea-0689-cd04b7b16135
# ╠═ce85691e-fc77-11ea-0aee-df1ba52cecd7
# ╠═ce85a7b2-fc77-11ea-2bec-0d7f0af3c59b
# ╟─b9c3f450-fcdb-11ea-2ca9-27f5dbb73a98
# ╟─aba487fe-fcdb-11ea-25e1-67c6f9596a16
# ╟─c62fab76-fcdb-11ea-1b75-e71f5c77eccf
# ╟─ce861b20-fc77-11ea-0b79-8dd922420260
# ╠═ce8df1ec-fc77-11ea-1c7d-ad620c19fbbd
# ╟─8a7c3e5c-8116-11eb-1911-b940b352dfc9
# ╠═8a7c7662-8116-11eb-3540-1dea64d7eec6
# ╠═8a7d461c-8116-11eb-043e-ffb6bf7d0737
# ╠═b7c70164-8116-11eb-0f7e-754d76c430ec
# ╠═bdc8090a-8116-11eb-07c8-d343006e2d79
# ╠═ce79d490-8116-11eb-0f1d-8d1719cf1e5b
# ╠═e4731414-8116-11eb-1340-0f2a41529348
# ╟─47566d90-fcda-11ea-0662-8fa0dd0583c4
# ╠═ce8e68c0-fc77-11ea-30eb-3fcbdd292d2c
# ╟─cea73280-fc77-11ea-348f-8973a4e7a5d3


text:
using BenchmarkTools
using Revise
using Base58
using Base64

code_typed(base58encode, (Array{UInt8, 1}, ))
code_llvm(base58encode, (Array{UInt8, 1}, ))
code_native(base58encode, (Array{UInt8, 1}, ))

code_typed(base58decode, (Array{UInt8, 1}, ))
code_llvm(base58decode, (Array{UInt8, 1}, ))
code_native(base58decode, (Array{UInt8, 1}, ))

base58decode([b"   11111"..., test_data[2, 7]...]) |> String
base58decode(b"     ")

base58encode(test_data[1, 4]);
base58encode(test_data[1, 7]);

@btime base58encode($(test_data[1, 7]));
@btime base64encode($(test_data[1, 7]));

@btime base58decode($(test_data[2, 7]));
@btime base64decode($(base64encode(test_data[1, 7])));

test_data = hcat(
    [b"",                b""],
    [[0x00],             b"1"],
    [[0x00, 0x00],       b"11"],
    [b"hello world",     b"StV1DL6CwTryKyV"],
    [b"\0\0hello world", b"11StV1DL6CwTryKyV"],
    [nothing,            b"3vQOB7B6uFg4oH"],
    [b""" !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~""",
                         b"3WSNuyEGf19K7EdeCmokbtTAXJwJUdvg8QXxAacYC7kR1bQoYeWVr5iMUHvxvv4FCFY48dVUrX6BrFLod6zsEhHU9NciUXFM17h1qtveYD7ocbnXQyuY84An9nAvEjdt6H"]
);

@btime base58encode($(test_data[1, 1]));
@btime base64encode($(test_data[1, 1]));

@btime base58encode($(test_data[1, 2]));
@btime base64encode($(test_data[1, 2]));

@btime base58encode($(test_data[1, 3]));
@btime base64encode($(test_data[1, 3]));

@btime base58encode($(test_data[1, 4]));
@btime base64encode($(test_data[1, 4]));

@btime base58encode($(test_data[1, 5]));
@btime base64encode($(test_data[1, 5]));

@btime base58encode($(test_data[1, 7]));
@btime base64encode($(test_data[1, 7]));

function count_leading_zeros(x)
    n_zeros = 0
    while n_zeros < length(x) && x[n_zeros + 1] == 0
        n_zeros += 1
    end
    n_zeros
end

function count_leading_zeros2(x)
    n_zeros = 0
    for c in x
        c != 0 && break
        n_zeros += 1
    end
    n_zeros
end

@btime count_leading_zeros($(test_data[1, 1]))
@btime count_leading_zeros2($(test_data[1, 1]))

@btime count_leading_zeros($(test_data[1, 2]))
@btime count_leading_zeros2($(test_data[1, 2]))

@btime count_leading_zeros($(test_data[1, 3]))
@btime count_leading_zeros2($(test_data[1, 3]))

@btime count_leading_zeros($(test_data[1, 4]))
@btime count_leading_zeros2($(test_data[1, 4]))

@btime count_leading_zeros($(test_data[1, 5]))
@btime count_leading_zeros2($(test_data[1, 5]))

@btime count_leading_zeros($(test_data[1, 7]))
@btime count_leading_zeros2($(test_data[1, 7]))


text:
using Test, SafeTestsets

@safetestset "FactorPostProcessing.jl test" begin include("svd_tests.jl") end


text:
monomialset(ctx::MonomialContext{M}, mons::Vector{M}) where M = SortedSet(mons, Base.Order.ReverseOrdering(order(ctx)))
monomialset(ctx::MonomialContext{M}) where M = monomialset(ctx, M[])
minmonomialset(ctx::MonomialContext{M}) where M = SortedSet(M[], order(ctx))

function find_reducer(ctx::SigPolynomialΓ{I, M},
                      G::Basis{I, M},
                      H::Syz{I, M},
                      m::M;
                      use_max_sig = false,
                      max_sig_pos = zero(pos_type(ctx)),
                      sig_degree = zero(exponenttype(ctx.po.mo)),
                      interreduction_step = false,
                      enable_lower_pos_rewrite = true) where {I, M}

    reducer = nothing
    mpairord = mpairordering(ctx)
    for (i, Gi) in G
        for (j, (g, lm)) in enumerate(Gi)
            g_sig = (i, g)
            if divides(ctx.po.mo, lm, m)
                delta = div(ctx.po.mo, m, lm)
                use_max_sig && ctx.ord_indices[i][:position] == max_sig_pos && degree(ctx, (delta, g_sig)) > sig_degree && continue
                if !(interreduction_step) && (enable_lower_pos_rewrite || ctx.ord_indices[i][:position] == max_sig_pos)
                    rewriteable(ctx, delta, g_sig, j, G, H) && continue
                end
                if !(interreduction_step) && (isnothing(reducer) || Base.Order.lt(mpairord, (delta, g_sig), reducer))
                    reducer = (delta, g_sig)
                end
                if interreduction_step && delta != Base.one(ctx.po.mo)
                    return (delta, g_sig)
                end
            end
        end
    end
    return reducer
end

function symbolic_pp!(ctx::SΓ,
                      pairs::MS,
                      G::Basis{I, M},
                      H::Syz{I, M};
                      use_max_sig = false,
                      sig_degree = zero(exponenttype(ctx.po.mo)),
                      max_sig_pos = zero(pos_type(ctx)),
                      are_pairs= true,
                      interreduction_step = false,
                      enable_lower_pos_rewrite = true) where {I, M <: Integer,
                                                              MS <: Union{MonSigSet{I, M}, Set{MonSigPair{I, M}}},
                                                              SΓ <: SigPolynomialΓ{I, M}}

    get_orig_elem = p -> interreduction_step || (!(enable_lower_pos_rewrite) && pos(ctx, p) < max_sig_pos)
    todo = Set{M}(vcat([ctx(p..., orig_elem = get_orig_elem(p))[:poly].mo for p in pairs]...))
    if are_pairs
        done = Set{M}([mul(ctx.po.mo, p[1], leadingmonomial(ctx, p[2])) for p in pairs])
    else
        done = Set(M[])
    end

    while todo != done
        for m in todo
            m in done && continue
            push!(done, m)
            red = find_reducer(ctx, G, H, m,
                               use_max_sig = use_max_sig,
                               max_sig_pos = max_sig_pos,
                               sig_degree = sig_degree,
                               interreduction_step = interreduction_step,
                               enable_lower_pos_rewrite = enable_lower_pos_rewrite)
            isnothing(red) && continue
            push!(pairs, red)
            union!(todo, ctx(red..., orig_elem = get_orig_elem(red))[:poly].mo)
        end
    end
    sort(collect(done), lt = (a, b) -> lt(ctx.po.mo, a, b), rev = true)
end


text:
export BrickletHumidityV2HumidityCallbackConfiguration
struct BrickletHumidityV2HumidityCallbackConfiguration
    period::Integer
    value_has_to_change::Bool
    option::Char
    min::Integer
    max::Integer
end

export BrickletHumidityV2TemperatureCallbackConfiguration
struct BrickletHumidityV2TemperatureCallbackConfiguration
    period::Integer
    value_has_to_change::Bool
    option::Char
    min::Integer
    max::Integer
end

export BrickletHumidityV2MovingAverageConfiguration
struct BrickletHumidityV2MovingAverageConfiguration
    moving_average_length_humidity::Integer
    moving_average_length_temperature::Integer
end

export BrickletHumidityV2SPITFPErrorCount
struct BrickletHumidityV2SPITFPErrorCount
    error_count_ack_checksum::Integer
    error_count_message_checksum::Integer
    error_count_frame::Integer
    error_count_overflow::Integer
end

export BrickletHumidityV2Identity
struct BrickletHumidityV2Identity
    uid::String
    connected_uid::String
    position::Char
    hardware_version::Vector{Integer}
    firmware_version::Vector{Integer}
    device_identifier::Integer
end

export BrickletHumidityV2
"""
Measures relative humidity
"""
mutable struct BrickletHumidityV2 <: TinkerforgeDevice
    ipcon::IPConnection
    deviceInternal::PyObject

    """
    Creates an object with the unique device ID *uid* and adds it to
    the IP Connection *ipcon*.
    """
    function BrickletHumidityV2(uid::String, ipcon::IPConnection)
        package = pyimport("tinkerforge.bricklet_humidity_v2")
        deviceInternal = package.BrickletHumidityV2(uid, ipcon.ipconInternal)

        return new(ipcon, deviceInternal)
    end
end

export get_humidity
"""
    $(SIGNATURES)

Returns the humidity measured by the sensor.

If you want to get the value periodically, it is recommended to use the
:cb:`Humidity` callback. You can set the callback configuration
with :func:`Set Humidity Callback Configuration`.
"""
function get_humidity(device::BrickletHumidityV2)
    return device.deviceInternal.get_humidity()
end

export set_humidity_callback_configuration
"""
    $(SIGNATURES)

The period is the period with which the :cb:`Humidity` callback is triggered
periodically. A value of 0 turns the callback off.

If the `value has to change`-parameter is set to true, the callback is only
triggered after the value has changed. If the value didn't change
within the period, the callback is triggered immediately on change.

If it is set to false, the callback is continuously triggered with the period,
independent of the value.

It is furthermore possible to constrain the callback with thresholds.

The `option`-parameter together with min/max sets a threshold for the :cb:`Humidity` callback.

The following options are possible:

.. csv-table::
 :header: "Option", "Description"
 :widths: 10, 100

 "'x'",    "Threshold is turned off"
 "'o'",    "Threshold is triggered when the value is *outside* the min and max values"
 "'i'",    "Threshold is triggered when the value is *inside* or equal to the min and max values"
 "'<'",    "Threshold is triggered when the value is smaller than the min value (max is ignored)"
 "'>'",    "Threshold is triggered when the value is greater than the min value (max is ignored)"

If the option is set to 'x' (threshold turned off) the callback is triggered with the fixed period.
"""
function set_humidity_callback_configuration(device::BrickletHumidityV2, period, value_has_to_change, option, min, max)
    device.deviceInternal.set_humidity_callback_configuration(period, value_has_to_change, option, min, max)
end

export get_humidity_callback_configuration
"""
    $(SIGNATURES)

Returns the callback configuration as set by :func:`Set Humidity Callback Configuration`.
"""
function get_humidity_callback_configuration(device::BrickletHumidityV2)
    return device.deviceInternal.get_humidity_callback_configuration()
end

export get_temperature
"""
    $(SIGNATURES)

Returns the temperature measured by the sensor.

If you want to get the value periodically, it is recommended to use the
:cb:`Temperature` callback. You can set the callback configuration
with :func:`Set Temperature Callback Configuration`.
"""
function get_temperature(device::BrickletHumidityV2)
    return device.deviceInternal.get_temperature()
end

export set_temperature_callback_configuration
"""
    $(SIGNATURES)

The period is the period with which the :cb:`Temperature` callback is triggered
periodically. A value of 0 turns the callback off.

If the `value has to change`-parameter is set to true, the callback is only
triggered after the value has changed. If the value didn't change
within the period, the callback is triggered immediately on change.

If it is set to false, the callback is continuously triggered with the period,
independent of the value.

It is furthermore possible to constrain the callback with thresholds.

The `option`-parameter together with min/max sets a threshold for the :cb:`Temperature` callback.

The following options are possible:

.. csv-table::
 :header: "Option", "Description"
 :widths: 10, 100

 "'x'",    "Threshold is turned off"
 "'o'",    "Threshold is triggered when the value is *outside* the min and max values"
 "'i'",    "Threshold is triggered when the value is *inside* or equal to the min and max values"
 "'<'",    "Threshold is triggered when the value is smaller than the min value (max is ignored)"
 "'>'",    "Threshold is triggered when the value is greater than the min value (max is ignored)"

If the option is set to 'x' (threshold turned off) the callback is triggered with the fixed period.
"""
function set_temperature_callback_configuration(device::BrickletHumidityV2, period, value_has_to_change, option, min, max)
    device.deviceInternal.set_temperature_callback_configuration(period, value_has_to_change, option, min, max)
end

export get_temperature_callback_configuration
"""
    $(SIGNATURES)

Returns the callback configuration as set by :func:`Set Temperature Callback Configuration`.
"""
function get_temperature_callback_configuration(device::BrickletHumidityV2)
    return device.deviceInternal.get_temperature_callback_configuration()
end

export set_heater_configuration
"""
    $(SIGNATURES)

Enables/disables the heater. The heater can be used to dry the sensor in
extremely wet conditions.
"""
function set_heater_configuration(device::BrickletHumidityV2, heater_config)
    device.deviceInternal.set_heater_configuration(heater_config)
end

export get_heater_configuration
"""
    $(SIGNATURES)

Returns the heater configuration as set by :func:`Set Heater Configuration`.
"""
function get_heater_configuration(device::BrickletHumidityV2)
    return device.deviceInternal.get_heater_configuration()
end

export set_moving_average_configuration
"""
    $(SIGNATURES)

Sets the length of a `moving averaging <https://en.wikipedia.org/wiki/Moving_average>`__
for the humidity and temperature.

Setting the length to 1 will turn the averaging off. With less
averaging, there is more noise on the data.

New data is gathered every 50ms*. With a moving average of length 1000 the resulting
averaging window has a length of 50s. If you want to do long term measurements the longest
moving average will give the cleanest results.

\\* In firmware version 2.0.3 we added the :func:`Set Samples Per Second` function. It
configures the measurement frequency. Since high frequencies can result in self-heating
of th IC, changed the default value from 20 samples per second to 1. With 1 sample per second
a moving average length of 1000 would result in an averaging window of 1000 seconds!
"""
function set_moving_average_configuration(device::BrickletHumidityV2, moving_average_length_humidity, moving_average_length_temperature)
    device.deviceInternal.set_moving_average_configuration(moving_average_length_humidity, moving_average_length_temperature)
end

export get_moving_average_configuration
"""
    $(SIGNATURES)

Returns the moving average configuration as set by :func:`Set Moving Average Configuration`.
"""
function get_moving_average_configuration(device::BrickletHumidityV2)
    return device.deviceInternal.get_moving_average_configuration()
end

export set_samples_per_second
"""
    $(SIGNATURES)

Sets the samples per second that are gathered by the humidity/temperature sensor HDC1080.

We added this function since we found out that a high measurement frequency can lead to
self-heating of the sensor. Which can distort the temperature measurement.

If you don't need a lot of measurements, you can use the lowest available measurement
frequency of 0.1 samples per second for the least amount of self-heating.

Before version 2.0.3 the default was 20 samples per second. The new default is 1 sample per second.

.. versionadded:: 2.0.3\$nbsp;(Plugin)
"""
function set_samples_per_second(device::BrickletHumidityV2, sps)
    device.deviceInternal.set_samples_per_second(sps)
end

export get_samples_per_second
"""
    $(SIGNATURES)

Returnes the samples per second, as set by :func:`Set Samples Per Second`.

.. versionadded:: 2.0.3\$nbsp;(Plugin)
"""
function get_samples_per_second(device::BrickletHumidityV2)
    return device.deviceInternal.get_samples_per_second()
end

export get_spitfp_error_count
"""
    $(SIGNATURES)

Returns the error count for the communication between Brick and Bricklet.

The errors are divided into

* ACK checksum errors,
* message checksum errors,
* framing errors and
* overflow errors.

The errors counts are for errors that occur on the Bricklet side. All
Bricks have a similar function that returns the errors on the Brick side.
"""
function get_spitfp_error_count(device::BrickletHumidityV2)
    return device.deviceInternal.get_spitfp_error_count()
end

export set_bootloader_mode
"""
    $(SIGNATURES)

Sets the bootloader mode and returns the status after the requested
mode change was instigated.

You can change from bootloader mode to firmware mode and vice versa. A change
from bootloader mode to firmware mode will only take place if the entry function,
device identifier and CRC are present and correct.

This function is used by Brick Viewer during flashing. It should not be
necessary to call it in a normal user program.
"""
function set_bootloader_mode(device::BrickletHumidityV2, mode)
    return device.deviceInternal.set_bootloader_mode(mode)
end

export get_bootloader_mode
"""
    $(SIGNATURES)

Returns the current bootloader mode, see :func:`Set Bootloader Mode`.
"""
function get_bootloader_mode(device::BrickletHumidityV2)
    return device.deviceInternal.get_bootloader_mode()
end

export set_write_firmware_pointer
"""
    $(SIGNATURES)

Sets the firmware pointer for :func:`Write Firmware`. The pointer has
to be increased by chunks of size 64. The data is written to flash
every 4 chunks (which equals to one page of size 256).

This function is used by Brick Viewer during flashing. It should not be
necessary to call it in a normal user program.
"""
function set_write_firmware_pointer(device::BrickletHumidityV2, pointer)
    device.deviceInternal.set_write_firmware_pointer(pointer)
end

export write_firmware
"""
    $(SIGNATURES)

Writes 64 Bytes of firmware at the position as written by
:func:`Set Write Firmware Pointer` before. The firmware is written
to flash every 4 chunks.

You can only write firmware in bootloader mode.

This function is used by Brick Viewer during flashing. It should not be
necessary to call it in a normal user program.
"""
function write_firmware(device::BrickletHumidityV2, data)
    return device.deviceInternal.write_firmware(data)
end

export set_status_led_config
"""
    $(SIGNATURES)

Sets the status LED configuration. By default the LED shows
communication traffic between Brick and Bricklet, it flickers once
for every 10 received data packets.

You can also turn the LED permanently on/off or show a heartbeat.

If the Bricklet is in bootloader mode, the LED is will show heartbeat by default.
"""
function set_status_led_config(device::BrickletHumidityV2, config)
    device.deviceInternal.set_status_led_config(config)
end

export get_status_led_config
"""
    $(SIGNATURES)

Returns the configuration as set by :func:`Set Status LED Config`
"""
function get_status_led_config(device::BrickletHumidityV2)
    return device.deviceInternal.get_status_led_config()
end

export get_chip_temperature
"""
    $(SIGNATURES)

Returns the temperature as measured inside the microcontroller. The
value returned is not the ambient temperature!

The temperature is only proportional to the real temperature and it has bad
accuracy. Practically it is only useful as an indicator for
temperature changes.
"""
function get_chip_temperature(device::BrickletHumidityV2)
    return device.deviceInternal.get_chip_temperature()
end

export reset
"""
    $(SIGNATURES)

Calling this function will reset the Bricklet. All configurations
will be lost.

After a reset you have to create new device objects,
calling functions on the existing ones will result in
undefined behavior!
"""
function reset(device::BrickletHumidityV2)
    device.deviceInternal.reset()
end

export write_uid
"""
    $(SIGNATURES)

Writes a new UID into flash. If you want to set a new UID
you have to decode the Base58 encoded UID string into an
integer first.

We recommend that you use Brick Viewer to change the UID.
"""
function write_uid(device::BrickletHumidityV2, uid)
    device.deviceInternal.write_uid(uid)
end

export read_uid
"""
    $(SIGNATURES)

Returns the current UID as an integer. Encode as
Base58 to get the usual string version.
"""
function read_uid(device::BrickletHumidityV2)
    return device.deviceInternal.read_uid()
end

export get_identity
"""
    $(SIGNATURES)

Returns the UID, the UID where the Bricklet is connected to,
the position, the hardware and firmware version as well as the
device identifier.

The position can be 'a', 'b', 'c', 'd', 'e', 'f', 'g' or 'h' (Bricklet Port).
A Bricklet connected to an :ref:`Isolator Bricklet <isolator_bricklet>` is always at
position 'z'.

The device identifier numbers can be found :ref:`here <device_identifier>`.
|device_identifier_constant|
"""
function get_identity(device::BrickletHumidityV2)
    return device.deviceInternal.get_identity()
end

export register_callback
"""
Registers the given *function* with the given *callback_id*.
"""
function register_callback(device::BrickletHumidityV2, callback_id, function_)
    if isnothing(function_)
        device.registered_callbacks.pop(callback_id, None)
    else
        device.registered_callbacks[callback_id] = function_
    end
end


text:
#
# This file is a part of MolecularGraph.jl
# Licensed under the MIT License http://opensource.org/licenses/MIT
#

export
    triangles

"""
    triangles(graph::UndirectedGraph) -> Set{Tuple{Int,Int,Int}}

Fast computation of finding triangle node sets in the graph.
"""
function triangles(graph::UndirectedGraph)
    triads = Set()
    for n in nodeset(graph)
        adjs = collect(adjacencies(graph, n))
        length(adjs) < 2 && continue
        for (e1, e2) in combinations(length(adjs))
            u, v = (adjs[e1], adjs[e2])
            if hasedge(graph, u, v)
                push!(triads, tuple(sort([n, u, v])...))
            end
        end
    end
    return triads
end


text:
using SimpleMatch
using Test

@test isempty(detect_ambiguities(SimpleMatch))

a = 1
b = 2.0
matched = @match(a, b) do f
  f(::Int, ::AbstractFloat) = b + 3
  f(args...) = a + 9
end
@test matched == 5.0


text:
# ---
# title: 640. Solve the Equation
# id: problem640
# author: Tian Jun
# date: 2020-10-31
# difficulty: Medium
# categories: Math
# link: <https://leetcode.com/problems/solve-the-equation/description/>
# hidden: true
# ---
#
# Solve a given equation and return the value of `x` in the form of string
# "x=#value". The equation contains only '+', '-' operation, the variable `x`
# and its coefficient.
#
# If there is no solution for the equation, return "No solution".
#
# If there are infinite solutions for the equation, return "Infinite solutions".
#
# If there is exactly one solution for the equation, we ensure that the value of
# `x` is an integer.
#
# **Example 1:**
#
#
#
#     Input: "x+5-3+x=6+x-2"
#     Output: "x=2"
#
#
# **Example 2:**
#
#
#
#     Input: "x=x"
#     Output: "Infinite solutions"
#
#
# **Example 3:**
#
#
#
#     Input: "2x=x"
#     Output: "x=0"
#
#
# **Example 4:**
#
#
#
#     Input: "2x+3x-6x=x+2"
#     Output: "x=-1"
#
#
# **Example 5:**
#
#
#
#     Input: "x=x+2"
#     Output: "No solution"
#
#
#
## @lc code=start
using LeetCode

## add your code here:
## @lc code=end


text:
#= -------------------------------------------------------------------------
# @file test_soulution.jl
#
# @date 03/11/16 20:06:13
# @author Martin Noblia
# @email martin.noblia@openmailbox.org
#
# @brief
#
# @detail
#
  Licence:
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or (at
# your option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License

---------------------------------------------------------------------------=#
include("./solution.jl")
#file_small = "../Files/A-ssmall-practice.in" Porque no anda los paths relativos
file_medium = "/home/elsuizo/CodeJam/Problema_store_credit/Files/A-small-practice.in"
file_small = "/home/elsuizo/CodeJam/Problema_store_credit/Files/A-ssmall-practice.in"
file_large = "/home/elsuizo/CodeJam/Problema_store_credit/Files/A-large-practice.in"

cases_vec = Credit.create_cases(file_large)

Credit.filter_cases(cases_vec)

max_stars_repo_name: elsuizo/CodeJam

max_issues_repo_name: elsuizo/CodeJam

max_forks_repo_name: elsuizo/CodeJam


text:
using .NetworkResilience
using Statistics

function main(t)

	ind = parse(Int64, ARGS[1])

	configs = []
	n = 40
	for j=1:n
		for i=1:n-j
			ne = n - i - j
			push!(configs, [i j ne])
		end
	end
	break1 = length(configs)
	# println(break1)

	# n = 100
	# for j=1:n
	# 	for i=1:n-j
	# 		ne = n - i - j
	# 		push!(configs, [i j ne])
	# 	end
	# end
	# # break2 = length(configs)
	# println(break1)
	# # println(break2-break1)
	# # println(break2)

	# # # n = 64
	# # # for j=1:n
	# # # 	for i=1:n-j
	# # # 		ne = n - i - j
	# # # 		push!(configs, [i j ne])
	# # # 	end
	# # # end
	# # # break3 = length(configs)

	#Make the file name
	directory = "redoneq0/"
	indexdict = Dict("40" => ind, "100" => ind-break1)
	subdirectorydict = Dict("40" => "40tri/", "100" => "100tri/")
	ntot = sum(configs[ind])
	thisid = indexdict[string(ntot)]
	thissubdir = subdirectorydict[string(ntot)]
	fname = "data/" * directory * thissubdir * string(thisid) * ".txt"

	#Compute the cascade
	ns = configs[ind][1]
	nd = configs[ind][2]
	ne = configs[ind][3]
	ensemblesize = 200
	# subensemblesize = 40
	tol = 5e-4
	n = ns + nd + ne
	q = 0.0
	k = 4
	Ptot = 1.0
	# ac, fmax, iter = cascadebisectionrandomtrigger(ensemblesize, subensemblesize, tol, n, ns, nd, q, k, Ptot)
	ac, fmax, iter = cascadebisection2(ensemblesize, tol, n, ns, nd, q, k, Ptot)

	open(fname, "w") do f
		write(f, "$ns $nd $ne $fmax \n")
		write(f, "$ac $fmax $iter $iter \n")
	end

	# ensemblesize = 1
	# topensemblesize = 2000
	# q = 0.1
	# alphares = 400
	# alphamin = 0.2
	# alphamax = 2.0
	# k = 4
	# Ptot = 1.0
	# Xval = 1.0
	# es = zeros(Float64, topensemblesize, alphares)
	# ts = zeros(Float64, topensemblesize, alphares)
	# as = zeros(Float64, alphares)
	# fmaxs = zeros(Float64, topensemblesize)
	# for i=1:topensemblesize

	# 	a, e, std, t, tstd, mate, matt, fmax = smcascadefixedtop(ensemblesize, ntot, ns, nd, q, alphares, alphamin, alphamax, k, Ptot, Xval)
	# 	es[i,:] = e
	# 	ts[i,:] = t
	# 	as[:] = a
	# 	fmaxs[i] = fmax

	# end

	# #Compute mean over the topolgy ensemble
	# eav = mean(es, dims=1)[1,:]
	# tav = mean(ts, dims=1)[1,:]
	# estd = std(es, dims=1)[1,:]
	# meanfmax = mean(fmaxs)

	#Write stuff to the file
	# open(fname, "w") do f
	# 	write(f, "$ns $nd $ne $meanfmax \n")
	# 	for i=1:alphares
	# 		beta = eav[i]
	# 		gamma = estd[i]
	# 		kappa = as[i]
	# 		epsilon = tav[i]
	# 		write(f, "$kappa $beta $gamma $epsilon \n")
	# 	end
	# end

end

main(ARGS)

max_stars_repo_name: o-smith/GridResilience

max_issues_repo_name: o-smith/GridResilience

max_forks_repo_name: o-smith/GridResilience


text:
using Merly
using HTTP
using BenchmarkTools

ip = "127.0.0.1"
port = 8086

function authenticate(request, HTTP)
  isAuthenticated = false

  if (request.params["status"] === "authenticated")
    isAuthenticated = true
  end

  return request, HTTP, isAuthenticated
end

Get("/verify/:status",

  (result(;middleware=authenticate) = (request, HTTP)-> begin

      myfunction = (request, HTTP, isAuthenticated)-> begin

      if (isAuthenticated == false )
          return  HTTP.Response(403,string("Unauthenticated. Please signup!"))
      end
                return  HTTP.Response(200,string("verify !"))
      end

      return myfunction(middleware(request,HTTP)...)

  end)()

)

@page "/" HTTP.Response(200,"Hello World!")

@page "/url1" HTTP.Response(200,"test1")
@page "/url2" HTTP.Response(200,"test2")
@page "/hola/:usr" HTTP.Response(200,"Hello {{usr}}!")

@page "/hola2/:usr" begin
	HTTP.Response(200,string("Hello2",request.params["usr"],"!"))
	end

@route GET "/get/:data1" begin
HTTP.Response(200,string("get this back:",request.params["data1"],"!"))
end

start(verbose=true)

#------------------04/02/2021-------------------------------
# @btime r=HTTP.request("GET", string("http://",ip,":",port,"/verify/authenticated"))
# 136.400 μs (535 allocations: 30.41 KiB)
# @btime r=HTTP.request("GET", string("http://",ip,":",port,"/"))
# 134.800 μs (527 allocations: 29.41 KiB)
# @btime r=HTTP.request("GET", string("http://",ip,":",port,"/?hola=5"))
# 137.600 μs (540 allocations: 30.63 KiB)
# @btime r=HTTP.request("GET", string("http://",ip,":",port,"/url2"))
# 135.500 μs (533 allocations: 29.78 KiB)
# @btime r=HTTP.request("GET", string("http://",ip,":",port,"/url1"))
# 135.900 μs (530 allocations: 29.61 KiB)
# @btime HTTP.get("http://$(ip):$(port)/hola/usuario")
# 137.199 μs (536 allocations: 30.41 KiB)
# @btime r= HTTP.get("http://$(ip):$(port)/get/testdata")
# 137.999 μs (539 allocations: 30.56 KiB)
#------------------02/02/2021-------------------------------
# @btime r=HTTP.request("GET", string("http://",ip,":",port,"/verify"))
# 118.799 μs (259 allocations: 15.50 KiB)
#------------------01/02/2021-------------------------------
# @btime r=HTTP.request("GET", string("http://",ip,":",port,"/verify"))
# 120.400 μs (259 allocations: 15.50 KiB)
#------------------12/01/2021-------------------------------
# @btime r=HTTP.request("GET", string("http://",ip,":",port,"/"))
# 119.701 μs (263 allocations: 15.20 KiB)
# @btime r=HTTP.request("GET", string("http://",ip,":",port,"/?hola=5"))
# 122.900 μs (251 allocations: 15.02 KiB)
# @btime r=HTTP.request("GET", string("http://",ip,":",port,"/urll2"))
# 118.100 μs (251 allocations: 15.00 KiB)
# @btime r=HTTP.request("GET", string("http://",ip,":",port,"/url2"))
# 120.199 μs (264 allocations: 15.27 KiB)
# @btime r=HTTP.request("GET", string("http://",ip,":",port,"/url2/"))
# 123.999 μs (251 allocations: 14.98 KiB)
# @btime HTTP.get("http://$(ip):$(port)/hola/usuario")
# 120.900 μs (264 allocations: 15.27 KiB)
# @btime r= HTTP.get("http://$(ip):$(port)/get/testdata")
# 122.500 μs (251 allocations: 15.05 KiB)

#------------------11/01/2021-------------------------------
# @btime r=HTTP.request("GET", string("http://",ip,":",port,"/"))
# 118.700 μs (250 allocations: 14.97 KiB) -- 11/01/2021
# 119.499 μs (251 allocations: 15.00 KiB) -- 11/01/2021 08:14 [ejecutando la funcion]
# @btime r=HTTP.request("GET", string("http://",ip,":",port,"/?hola=5"))
# 132.400 μs (251 allocations: 15.00 KiB) -- 11/01/2021 08:14 [ejecutando la funcion]
# @btime r=HTTP.request("GET", string("http://",ip,":",port,"/urll2"))
# 123.900 μs (251 allocations: 15.00 KiB) [ejecutando la funcion]
# @btime r=HTTP.request("GET", string("http://",ip,":",port,"/url2"))
# 122.100 μs (251 allocations: 14.98 KiB) [ejecutando la funcion]

#------------------02/01/2021-------------------------------
# @btime r=HTTP.request("GET", string("http://",ip,":",port,"/"))
# 80.100 μs (247 allocations: 14.84 KiB) [minimo]
# 82.400 μs (247 allocations: 14.84 KiB) [sin la ejecucion de la funcion]
# 83.600 μs (247 allocations: 14.84 KiB) -- 09/01/2021 [sin la ejecucion de la funcion]
# 114.199 μs (250 allocations: 14.97 KiB) [ejecutando la funcion]
# @btime r=HTTP.request("GET", string("http://",ip,":",port,"/?hola=5"))
# 91.600 μs (247 allocations: 14.86 KiB)
# 93.900 μs (247 allocations: 14.86 KiB)[sin la ejecucion de la funcion]

#------------------01/01/2021-------------------------------
# @btime r= HTTP.get(string("http://",ip,":",port,"/"))
# 115.700 μs (272 allocations: 15.70 KiB)
# @btime HTTP.get(string("http://",ip,":",port,"/?hola=5"))
# 125.200 μs (273 allocations: 15.77 KiB)
# @btime HTTP.get(string("http://",ip,":",port,"/hola/usuario"))
# 150.600 μs (259 allocations: 15.50 KiB)
# @btime r= HTTP.get(string("http://",ip,":",port,"/get/testdata"))
# 144.700 μs (259 allocations: 15.50 KiB)

#------------------31/12/2020-------------------------------
# @btime r= HTTP.get("http://$(ip):$(port)/")
# 131.100 μs (539 allocations: 30.03 KiB)
# @btime HTTP.get("http://$(ip):$(port)/?hola=5")
# 140.601 μs (574 allocations: 32.75 KiB)
# @btime HTTP.get("http://$(ip):$(port)/hola/usuario")
# 165.399 μs (568 allocations: 31.61 KiB)
# @btime r= HTTP.get("http://$(ip):$(port)/get/testdata")
# 165.300 μs (564 allocations: 31.50 KiB)

#------------------29/12/2020-------------------------------
# @btime r= HTTP.get("http://$(ip):$(port)/")
# 125.899 μs (510 allocations: 29.94 KiB)
# @btime HTTP.get("http://$(ip):$(port)/?hola=5")
# 144.100 μs (575 allocations: 32.80 KiB)
# @btime HTTP.get("http://$(ip):$(port)/hola/usuario")
# 171.801 μs (569 allocations: 31.66 KiB)
# @btime r= HTTP.get("http://$(ip):$(port)/get/testdata")
#  164.800 μs (565 allocations: 31.55 KiB)

#------------------27/12/2020-------------------------------
# @btime r= HTTP.get("http://$(ip):$(port)/")
# 132.400 μs (540 allocations: 30.08 KiB)
# @btime HTTP.get("http://$(ip):$(port)/?hola=5")
# 141.000 μs (575 allocations: 32.80 KiB)
# @btime HTTP.get("http://$(ip):$(port)/hola/usuario")
# 164.300 μs (563 allocations: 31.50 KiB)
# @btime r= HTTP.get("http://$(ip):$(port)/get/testdata")
# 165.600 μs (564 allocations: 31.53 KiB)

#------------------26/12/2020-------------------------------
# @btime HTTP.get("http://$(ip):$(port)/?hola=5")
# 140.401 μs (575 allocations: 32.80 KiB)
# @btime HTTP.get("http://$(ip):$(port)/hola/usuario")
# 163.801 μs (563 allocations: 31.50 KiB)
# @btime r= HTTP.get("http://$(ip):$(port)/get/testdata")
# 163.799 μs (522 allocations: 31.17 KiB)

#-------------------------------------------------
#@btime HTTP.get("http://$(ip):$(port)/?hola=5")
# 3.864 ms (8304 allocations: 381.20 KiB)
# 453.803 μs (748 allocations: 30.92 KiB)
#@btime HTTP.get("http://$(ip):$(port)/hola/usuario")
# 4.211 ms (7685 allocations: 353.44 KiB)
# 483.861 μs (743 allocations: 30.41 KiB)
#@benchmark r= HTTP.get("http://$(ip):$(port)/get/testdata")
# 3.906 ms (7693 allocations: 353.78 KiB)
# 484.960 μs (744 allocations: 30.44 KiB)
#=
 minimum time:     740.087 μs (0.00% GC)
  median time:      843.457 μs (0.00% GC)
  mean time:        1.099 ms (1.45% GC)
  maximum time:     367.611 ms (0.00% GC)
=#


text:
using DrWatson
@quickactivate "StatReth"

# %%
ENV["GKS_ENCODING"] = "utf-8" # Allows the use of unicode characters in Plots.jl
using Plots
using Turing
using CSV
using StatsPlots

# %%
df = CSV.read(datadir("exp_raw/Howell_1.csv"))

# Use only adults and center the weight observations
df2 = filter(row -> row.age >= 18, df)
mean_weight = mean(df2.weight)
df2.weight_c = df2.weight .- mean_weight
first(df2, 5)

# Extract variables for Turing model

x = df2.weight_c
y = df2.height

# Define the regression model

@model line(x, y) = begin
    #priors
    height0 ~ Normal(178.0, 100.0)
    steigung ~ Normal(0.0, 10.0)
    s ~ Uniform(0, 50)

    #model
    mu = height0 .+ steigung .* x
    y .~ Normal.(mu, s)
    return mu, y
end

# Draw the samples
model = line(x, y)

chns = sample(model, NUTS(0.65), 1000)

describe(chns) |> display

plot(chns)

# %%
varinfo = Turing.VarInfo(prior)
spl = Turing.SampleFromPrior()

@code_warntype model.f(varinfo, spl, Turing.DefaultContext(), model)

prior = line(collect(30.0:70.0), missing)

# %%
plot(legend = false)

for i in 1:10
    # pmu, py = prior()
    # plot!(30.0:70.0, pmu, c = 1)
    # plot!(30.0:70.0, py, c = 3)

    mmu, my = model()
    # plot!(x, mmu, c = 2)
    scatter!(x, my, c = 4)
end
scatter!(x, y)


text:
include("../../mesh.jl")
include("../../utils.jl")
include("../../connectique.jl")
include("../../visu.jl")

function main()
    println("#### Validation maillage ####")
    # toy problem
    vtx = collect(transpose(
        Float64[1.0000    1.0000;
        0.9986    0.5823;
        0.8514    0.7718;
        0.8245    1.0000;
        0.7638    0.6373;
        0.7967    0.5113;
        0.5610    0.2393;
        1.0000    0.2083;
        1.0000   -0.0000;
        0.5663    0.9447;
        0.5567    1.0000;
        0.0000    1.0000;
        0.4946    0.2228;
        0.4691    0.0000;
        0.0000    0.5989;
       -0.0000    0.0000]
    ))
    elt = [
        Int64[15, 13, 10, 11, 12],
        Int64[3, 2, 1, 4],
        Int64[5, 3, 4, 11, 10],
        Int64[7, 8, 2, 6],
        Int64[13, 7, 6, 5, 10],
        Int64[16, 14, 13, 15],
        Int64[6, 2, 3, 5],
        Int64[14, 9, 8, 7, 13]
    ];
    # compute neighbours
    e2neigh, _ = getNeighbours(vtx, elt)
    nedg    = getNbEdges(e2neigh)
    # compute free nodes and edges
    e2edg, freeedg, freenod = getMeshEdges(vtx, elt, nedg, e2neigh)
    #
    data = collect(range(0, stop=1., length=length(elt)));
    plotSolutionElt(mesh(vtx, elt, e2edg, freeedg, freenod, zeros(Int64, 2), nedg), data)
    nothing
end

main()