using JuMP, EAGO

                     m = Model()

                     EAGO.register_eago_operators!(m)

                     @variable(m, -1 <= x[i=1:4] <= 1)

                     @variable(m, -41.21023060076703 <= q <= 39.01192102445897)

                     add_NL_constraint(m, :(swish(-0.296746779395487 + 0.5509866377924739*swish(-0.36537726371933 + -0.13236748618316563*swish(0.24726246482452874 + -0.2513871312104543*swish(-0.7954573007956682 + 0.11924049292543248*$(x[1]) + 0.1731334424011517*$(x[2]) + -0.6758597754938092*$(x[3]) + 0.13301420625435734*$(x[4])) + 0.4677853214537917*swish(-0.7274702539948783 + 0.17282628886946716*$(x[1]) + 0.018302754525115095*$(x[2]) + 0.800298561254523*$(x[3]) + -0.4821070372998202*$(x[4])) + -0.10088601735084568*swish(-0.4299065646425082 + -0.1572016654178685*$(x[1]) + 0.08953552697871814*$(x[2]) + 0.5995804212147959*$(x[3]) + -0.3420773339437706*$(x[4])) + 0.6507022966434817*swish(0.440020919910439 + 0.27581141739825465*$(x[1]) + 0.9904718804451909*$(x[2]) + -0.9775405312317109*$(x[3]) + 0.5010243235294203*$(x[4]))) + 0.8938439926067105*swish(0.30866893537934104 + -0.11067617174498023*swish(-0.7954573007956682 + 0.11924049292543248*$(x[1]) + 0.1731334424011517*$(x[2]) + -0.6758597754938092*$(x[3]) + 0.13301420625435734*$(x[4])) + 0.7749548972944802*swish(-0.7274702539948783 + 0.17282628886946716*$(x[1]) + 0.018302754525115095*$(x[2]) + 0.800298561254523*$(x[3]) + -0.4821070372998202*$(x[4])) + 0.44318801341623093*swish(-0.4299065646425082 + -0.1572016654178685*$(x[1]) + 0.08953552697871814*$(x[2]) + 0.5995804212147959*$(x[3]) + -0.3420773339437706*$(x[4])) + 0.23547099627350976*swish(0.440020919910439 + 0.27581141739825465*$(x[1]) + 0.9904718804451909*$(x[2]) + -0.9775405312317109*$(x[3]) + 0.5010243235294203*$(x[4]))) + -0.2026514795524368*swish(-0.39572563026399044 + -0.2902839675147266*swish(-0.7954573007956682 + 0.11924049292543248*$(x[1]) + 0.1731334424011517*$(x[2]) + -0.6758597754938092*$(x[3]) + 0.13301420625435734*$(x[4])) + 0.2975675315855191*swish(-0.7274702539948783 + 0.17282628886946716*$(x[1]) + 0.018302754525115095*$(x[2]) + 0.800298561254523*$(x[3]) + -0.4821070372998202*$(x[4])) + 0.808554335286296*swish(-0.4299065646425082 + -0.1572016654178685*$(x[1]) + 0.08953552697871814*$(x[2]) + 0.5995804212147959*$(x[3]) + -0.3420773339437706*$(x[4])) + -0.2217074058396178*swish(0.440020919910439 + 0.27581141739825465*$(x[1]) + 0.9904718804451909*$(x[2]) + -0.9775405312317109*$(x[3]) + 0.5010243235294203*$(x[4]))) + 0.4969096390589627*swish(0.8092602903578396 + -0.8861475410157751*swish(-0.7954573007956682 + 0.11924049292543248*$(x[1]) + 0.1731334424011517*$(x[2]) + -0.6758597754938092*$(x[3]) + 0.13301420625435734*$(x[4])) + -0.38211269748902854*swish(-0.7274702539948783 + 0.17282628886946716*$(x[1]) + 0.018302754525115095*$(x[2]) + 0.800298561254523*$(x[3]) + -0.4821070372998202*$(x[4])) + 0.47211467596801615*swish(-0.4299065646425082 + -0.1572016654178685*$(x[1]) + 0.08953552697871814*$(x[2]) + 0.5995804212147959*$(x[3]) + -0.3420773339437706*$(x[4])) + -0.8428121402295612*swish(0.440020919910439 + 0.27581141739825465*$(x[1]) + 0.9904718804451909*$(x[2]) + -0.9775405312317109*$(x[3]) + 0.5010243235294203*$(x[4])))) + -0.850208463612319*swish(-0.13805861779585626 + 0.8324190447258686*swish(0.24726246482452874 + -0.2513871312104543*swish(-0.7954573007956682 + 0.11924049292543248*$(x[1]) + 0.1731334424011517*$(x[2]) + -0.6758597754938092*$(x[3]) + 0.13301420625435734*$(x[4])) + 0.4677853214537917*swish(-0.7274702539948783 + 0.17282628886946716*$(x[1]) + 0.018302754525115095*$(x[2]) + 0.800298561254523*$(x[3]) + -0.4821070372998202*$(x[4])) + -0.10088601735084568*swish(-0.4299065646425082 + -0.1572016654178685*$(x[1]) + 0.08953552697871814*$(x[2]) + 0.5995804212147959*$(x[3]) + -0.3420773339437706*$(x[4])) + 0.6507022966434817*swish(0.440020919910439 + 0.27581141739825465*$(x[1]) + 0.9904718804451909*$(x[2]) + -0.9775405312317109*$(x[3]) + 0.5010243235294203*$(x[4]))) + 0.45691763611374503*swish(0.30866893537934104 + -0.11067617174498023*swish(-0.7954573007956682 + 0.11924049292543248*$(x[1]) + 0.1731334424011517*$(x[2]) + -0.6758597754938092*$(x[3]) + 0.13301420625435734*$(x[4])) + 0.7749548972944802*swish(-0.7274702539948783 + 0.17282628886946716*$(x[1]) + 0.018302754525115095*$(x[2]) + 0.800298561254523*$(x[3]) + -0.4821070372998202*$(x[4])) + 0.44318801341623093*swish(-0.4299065646425082 + -0.1572016654178685*$(x[1]) + 0.08953552697871814*$(x[2]) + 0.5995804212147959*$(x[3]) + -0.3420773339437706*$(x[4])) + 0.23547099627350976*swish(0.440020919910439 + 0.27581141739825465*$(x[1]) + 0.9904718804451909*$(x[2]) + -0.9775405312317109*$(x[3]) + 0.5010243235294203*$(x[4]))) + -0.0018061937798208305*swish(-0.39572563026399044 + -0.2902839675147266*swish(-0.7954573007956682 + 0.11924049292543248*$(x[1]) + 0.1731334424011517*$(x[2]) + -0.6758597754938092*$(x[3]) + 0.13301420625435734*$(x[4])) + 0.2975675315855191*swish(-0.7274702539948783 + 0.17282628886946716*$(x[1]) + 0.018302754525115095*$(x[2]) + 0.800298561254523*$(x[3]) + -0.4821070372998202*$(x[4])) + 0.808554335286296*swish(-0.4299065646425082 + -0.1572016654178685*$(x[1]) + 0.08953552697871814*$(x[2]) + 0.5995804212147959*$(x[3]) + -0.3420773339437706*$(x[4])) + -0.2217074058396178*swish(0.440020919910439 + 0.27581141739825465*$(x[1]) + 0.9904718804451909*$(x[2]) + -0.9775405312317109*$(x[3]) + 0.5010243235294203*$(x[4]))) + -0.06838129719944464*swish(0.8092602903578396 + -0.8861475410157751*swish(-0.7954573007956682 + 0.11924049292543248*$(x[1]) + 0.1731334424011517*$(x[2]) + -0.6758597754938092*$(x[3]) + 0.13301420625435734*$(x[4])) + -0.38211269748902854*swish(-0.7274702539948783 + 0.17282628886946716*$(x[1]) + 0.018302754525115095*$(x[2]) + 0.800298561254523*$(x[3]) + -0.4821070372998202*$(x[4])) + 0.47211467596801615*swish(-0.4299065646425082 + -0.1572016654178685*$(x[1]) + 0.08953552697871814*$(x[2]) + 0.5995804212147959*$(x[3]) + -0.3420773339437706*$(x[4])) + -0.8428121402295612*swish(0.440020919910439 + 0.27581141739825465*$(x[1]) + 0.9904718804451909*$(x[2]) + -0.9775405312317109*$(x[3]) + 0.5010243235294203*$(x[4])))) + 0.14361846152386626*swish(0.892089992500452 + -0.9262441239021939*swish(0.24726246482452874 + -0.2513871312104543*swish(-0.7954573007956682 + 0.11924049292543248*$(x[1]) + 0.1731334424011517*$(x[2]) + -0.6758597754938092*$(x[3]) + 0.13301420625435734*$(x[4])) + 0.4677853214537917*swish(-0.7274702539948783 + 0.17282628886946716*$(x[1]) + 0.018302754525115095*$(x[2]) + 0.800298561254523*$(x[3]) + -0.4821070372998202*$(x[4])) + -0.10088601735084568*swish(-0.4299065646425082 + -0.1572016654178685*$(x[1]) + 0.08953552697871814*$(x[2]) + 0.5995804212147959*$(x[3]) + -0.3420773339437706*$(x[4])) + 0.6507022966434817*swish(0.440020919910439 + 0.27581141739825465*$(x[1]) + 0.9904718804451909*$(x[2]) + -0.9775405312317109*$(x[3]) + 0.5010243235294203*$(x[4]))) + -0.1220711338530176*swish(0.30866893537934104 + -0.11067617174498023*swish(-0.7954573007956682 + 0.11924049292543248*$(x[1]) + 0.1731334424011517*$(x[2]) + -0.6758597754938092*$(x[3]) + 0.13301420625435734*$(x[4])) + 0.7749548972944802*swish(-0.7274702539948783 + 0.17282628886946716*$(x[1]) + 0.018302754525115095*$(x[2]) + 0.800298561254523*$(x[3]) + -0.4821070372998202*$(x[4])) + 0.44318801341623093*swish(-0.4299065646425082 + -0.1572016654178685*$(x[1]) + 0.08953552697871814*$(x[2]) + 0.5995804212147959*$(x[3]) + -0.3420773339437706*$(x[4])) + 0.23547099627350976*swish(0.440020919910439 + 0.27581141739825465*$(x[1]) + 0.9904718804451909*$(x[2]) + -0.9775405312317109*$(x[3]) + 0.5010243235294203*$(x[4]))) + 0.013243228467184842*swish(-0.39572563026399044 + -0.2902839675147266*swish(-0.7954573007956682 + 0.11924049292543248*$(x[1]) + 0.1731334424011517*$(x[2]) + -0.6758597754938092*$(x[3]) + 0.13301420625435734*$(x[4])) + 0.2975675315855191*swish(-0.7274702539948783 + 0.17282628886946716*$(x[1]) + 0.018302754525115095*$(x[2]) + 0.800298561254523*$(x[3]) + -0.4821070372998202*$(x[4])) + 0.808554335286296*swish(-0.4299065646425082 + -0.1572016654178685*$(x[1]) + 0.08953552697871814*$(x[2]) + 0.5995804212147959*$(x[3]) + -0.3420773339437706*$(x[4])) + -0.2217074058396178*swish(0.440020919910439 + 0.27581141739825465*$(x[1]) + 0.9904718804451909*$(x[2]) + -0.9775405312317109*$(x[3]) + 0.5010243235294203*$(x[4]))) + 0.22830737094249542*swish(0.8092602903578396 + -0.8861475410157751*swish(-0.7954573007956682 + 0.11924049292543248*$(x[1]) + 0.1731334424011517*$(x[2]) + -0.6758597754938092*$(x[3]) + 0.13301420625435734*$(x[4])) + -0.38211269748902854*swish(-0.7274702539948783 + 0.17282628886946716*$(x[1]) + 0.018302754525115095*$(x[2]) + 0.800298561254523*$(x[3]) + -0.4821070372998202*$(x[4])) + 0.47211467596801615*swish(-0.4299065646425082 + -0.1572016654178685*$(x[1]) + 0.08953552697871814*$(x[2]) + 0.5995804212147959*$(x[3]) + -0.3420773339437706*$(x[4])) + -0.8428121402295612*swish(0.440020919910439 + 0.27581141739825465*$(x[1]) + 0.9904718804451909*$(x[2]) + -0.9775405312317109*$(x[3]) + 0.5010243235294203*$(x[4])))) + 0.12455164844812394*swish(-0.09971606472325156 + 0.22050596868305217*swish(0.24726246482452874 + -0.2513871312104543*swish(-0.7954573007956682 + 0.11924049292543248*$(x[1]) + 0.1731334424011517*$(x[2]) + -0.6758597754938092*$(x[3]) + 0.13301420625435734*$(x[4])) + 0.4677853214537917*swish(-0.7274702539948783 + 0.17282628886946716*$(x[1]) + 0.018302754525115095*$(x[2]) + 0.800298561254523*$(x[3]) + -0.4821070372998202*$(x[4])) + -0.10088601735084568*swish(-0.4299065646425082 + -0.1572016654178685*$(x[1]) + 0.08953552697871814*$(x[2]) + 0.5995804212147959*$(x[3]) + -0.3420773339437706*$(x[4])) + 0.6507022966434817*swish(0.440020919910439 + 0.27581141739825465*$(x[1]) + 0.9904718804451909*$(x[2]) + -0.9775405312317109*$(x[3]) + 0.5010243235294203*$(x[4]))) + -0.9537528744420904*swish(0.30866893537934104 + -0.11067617174498023*swish(-0.7954573007956682 + 0.11924049292543248*$(x[1]) + 0.1731334424011517*$(x[2]) + -0.6758597754938092*$(x[3]) + 0.13301420625435734*$(x[4])) + 0.7749548972944802*swish(-0.7274702539948783 + 0.17282628886946716*$(x[1]) + 0.018302754525115095*$(x[2]) + 0.800298561254523*$(x[3]) + -0.4821070372998202*$(x[4])) + 0.44318801341623093*swish(-0.4299065646425082 + -0.1572016654178685*$(x[1]) + 0.08953552697871814*$(x[2]) + 0.5995804212147959*$(x[3]) + -0.3420773339437706*$(x[4])) + 0.23547099627350976*swish(0.440020919910439 + 0.27581141739825465*$(x[1]) + 0.9904718804451909*$(x[2]) + -0.9775405312317109*$(x[3]) + 0.5010243235294203*$(x[4]))) + -0.13795746696872868*swish(-0.39572563026399044 + -0.2902839675147266*swish(-0.7954573007956682 + 0.11924049292543248*$(x[1]) + 0.1731334424011517*$(x[2]) + -0.6758597754938092*$(x[3]) + 0.13301420625435734*$(x[4])) + 0.2975675315855191*swish(-0.7274702539948783 + 0.17282628886946716*$(x[1]) + 0.018302754525115095*$(x[2]) + 0.800298561254523*$(x[3]) + -0.4821070372998202*$(x[4])) + 0.808554335286296*swish(-0.4299065646425082 + -0.1572016654178685*$(x[1]) + 0.08953552697871814*$(x[2]) + 0.5995804212147959*$(x[3]) + -0.3420773339437706*$(x[4])) + -0.2217074058396178*swish(0.440020919910439 + 0.27581141739825465*$(x[1]) + 0.9904718804451909*$(x[2]) + -0.9775405312317109*$(x[3]) + 0.5010243235294203*$(x[4]))) + 0.0643190431003462*swish(0.8092602903578396 + -0.8861475410157751*swish(-0.7954573007956682 + 0.11924049292543248*$(x[1]) + 0.1731334424011517*$(x[2]) + -0.6758597754938092*$(x[3]) + 0.13301420625435734*$(x[4])) + -0.38211269748902854*swish(-0.7274702539948783 + 0.17282628886946716*$(x[1]) + 0.018302754525115095*$(x[2]) + 0.800298561254523*$(x[3]) + -0.4821070372998202*$(x[4])) + 0.47211467596801615*swish(-0.4299065646425082 + -0.1572016654178685*$(x[1]) + 0.08953552697871814*$(x[2]) + 0.5995804212147959*$(x[3]) + -0.3420773339437706*$(x[4])) + -0.8428121402295612*swish(0.440020919910439 + 0.27581141739825465*$(x[1]) + 0.9904718804451909*$(x[2]) + -0.9775405312317109*$(x[3]) + 0.5010243235294203*$(x[4]))))) + swish(-0.7906046812278151 + -0.2168623555923963*swish(-0.36537726371933 + -0.13236748618316563*swish(0.24726246482452874 + -0.2513871312104543*swish(-0.7954573007956682 + 0.11924049292543248*$(x[1]) + 0.1731334424011517*$(x[2]) + -0.6758597754938092*$(x[3]) + 0.13301420625435734*$(x[4])) + 0.4677853214537917*swish(-0.7274702539948783 + 0.17282628886946716*$(x[1]) + 0.018302754525115095*$(x[2]) + 0.800298561254523*$(x[3]) + -0.4821070372998202*$(x[4])) + -0.10088601735084568*swish(-0.4299065646425082 + -0.1572016654178685*$(x[1]) + 0.08953552697871814*$(x[2]) + 0.5995804212147959*$(x[3]) + -0.3420773339437706*$(x[4])) + 0.6507022966434817*swish(0.440020919910439 + 0.27581141739825465*$(x[1]) + 0.9904718804451909*$(x[2]) + -0.9775405312317109*$(x[3]) + 0.5010243235294203*$(x[4]))) + 0.8938439926067105*swish(0.30866893537934104 + -0.11067617174498023*swish(-0.7954573007956682 + 0.11924049292543248*$(x[1]) + 0.1731334424011517*$(x[2]) + -0.6758597754938092*$(x[3]) + 0.13301420625435734*$(x[4])) + 0.7749548972944802*swish(-0.7274702539948783 + 0.17282628886946716*$(x[1]) + 0.018302754525115095*$(x[2]) + 0.800298561254523*$(x[3]) + -0.4821070372998202*$(x[4])) + 0.44318801341623093*swish(-0.4299065646425082 + -0.1572016654178685*$(x[1]) + 0.08953552697871814*$(x[2]) + 0.5995804212147959*$(x[3]) + -0.3420773339437706*$(x[4])) + 0.23547099627350976*swish(0.440020919910439 + 0.27581141739825465*$(x[1]) + 0.9904718804451909*$(x[2]) + -0.9775405312317109*$(x[3]) + 0.5010243235294203*$(x[4]))) + -0.2026514795524368*swish(-0.39572563026399044 + -0.2902839675147266*swish(-0.7954573007956682 + 0.11924049292543248*$(x[1]) + 0.1731334424011517*$(x[2]) + -0.6758597754938092*$(x[3]) + 0.13301420625435734*$(x[4])) + 0.2975675315855191*swish(-0.7274702539948783 + 0.17282628886946716*$(x[1]) + 0.018302754525115095*$(x[2]) + 0.800298561254523*$(x[3]) + -0.4821070372998202*$(x[4])) + 0.808554335286296*swish(-0.4299065646425082 + -0.1572016654178685*$(x[1]) + 0.08953552697871814*$(x[2]) + 0.5995804212147959*$(x[3]) + -0.3420773339437706*$(x[4])) + -0.2217074058396178*swish(0.440020919910439 + 0.27581141739825465*$(x[1]) + 0.9904718804451909*$(x[2]) + -0.9775405312317109*$(x[3]) + 0.5010243235294203*$(x[4]))) + 0.4969096390589627*swish(0.8092602903578396 + -0.8861475410157751*swish(-0.7954573007956682 + 0.11924049292543248*$(x[1]) + 0.1731334424011517*$(x[2]) + -0.6758597754938092*$(x[3]) + 0.13301420625435734*$(x[4])) + -0.38211269748902854*swish(-0.7274702539948783 + 0.17282628886946716*$(x[1]) + 0.018302754525115095*$(x[2]) + 0.800298561254523*$(x[3]) + -0.4821070372998202*$(x[4])) + 0.47211467596801615*swish(-0.4299065646425082 + -0.1572016654178685*$(x[1]) + 0.08953552697871814*$(x[2]) + 0.5995804212147959*$(x[3]) + -0.3420773339437706*$(x[4])) + -0.8428121402295612*swish(0.440020919910439 + 0.27581141739825465*$(x[1]) + 0.9904718804451909*$(x[2]) + -0.9775405312317109*$(x[3]) + 0.5010243235294203*$(x[4])))) + 0.3937942437507238*swish(-0.13805861779585626 + 0.8324190447258686*swish(0.24726246482452874 + -0.2513871312104543*swish(-0.7954573007956682 + 0.11924049292543248*$(x[1]) + 0.1731334424011517*$(x[2]) + -0.6758597754938092*$(x[3]) + 0.13301420625435734*$(x[4])) + 0.4677853214537917*swish(-0.7274702539948783 + 0.17282628886946716*$(x[1]) + 0.018302754525115095*$(x[2]) + 0.800298561254523*$(x[3]) + -0.4821070372998202*$(x[4])) + -0.10088601735084568*swish(-0.4299065646425082 + -0.1572016654178685*$(x[1]) + 0.08953552697871814*$(x[2]) + 0.5995804212147959*$(x[3]) + -0.3420773339437706*$(x[4])) + 0.6507022966434817*swish(0.440020919910439 + 0.27581141739825465*$(x[1]) + 0.9904718804451909*$(x[2]) + -0.9775405312317109*$(x[3]) + 0.5010243235294203*$(x[4]))) + 0.45691763611374503*swish(0.30866893537934104 + -0.11067617174498023*swish(-0.7954573007956682 + 0.11924049292543248*$(x[1]) + 0.1731334424011517*$(x[2]) + -0.6758597754938092*$(x[3]) + 0.13301420625435734*$(x[4])) + 0.7749548972944802*swish(-0.7274702539948783 + 0.17282628886946716*$(x[1]) + 0.018302754525115095*$(x[2]) + 0.800298561254523*$(x[3]) + -0.4821070372998202*$(x[4])) + 0.44318801341623093*swish(-0.4299065646425082 + -0.1572016654178685*$(x[1]) + 0.08953552697871814*$(x[2]) + 0.5995804212147959*$(x[3]) + -0.3420773339437706*$(x[4])) + 0.23547099627350976*swish(0.440020919910439 + 0.27581141739825465*$(x[1]) + 0.9904718804451909*$(x[2]) + -0.9775405312317109*$(x[3]) + 0.5010243235294203*$(x[4]))) + -0.0018061937798208305*swish(-0.39572563026399044 + -0.2902839675147266*swish(-0.7954573007956682 + 0.11924049292543248*$(x[1]) + 0.1731334424011517*$(x[2]) + -0.6758597754938092*$(x[3]) + 0.13301420625435734*$(x[4])) + 0.2975675315855191*swish(-0.7274702539948783 + 0.17282628886946716*$(x[1]) + 0.018302754525115095*$(x[2]) + 0.800298561254523*$(x[3]) + -0.4821070372998202*$(x[4])) + 0.808554335286296*swish(-0.4299065646425082 + -0.1572016654178685*$(x[1]) + 0.08953552697871814*$(x[2]) + 0.5995804212147959*$(x[3]) + -0.3420773339437706*$(x[4])) + -0.2217074058396178*swish(0.440020919910439 + 0.27581141739825465*$(x[1]) + 0.9904718804451909*$(x[2]) + -0.9775405312317109*$(x[3]) + 0.5010243235294203*$(x[4]))) + -0.06838129719944464*swish(0.8092602903578396 + -0.8861475410157751*swish(-0.7954573007956682 + 0.11924049292543248*$(x[1]) + 0.1731334424011517*$(x[2]) + -0.6758597754938092*$(x[3]) + 0.13301420625435734*$(x[4])) + -0.38211269748902854*swish(-0.7274702539948783 + 0.17282628886946716*$(x[1]) + 0.018302754525115095*$(x[2]) + 0.800298561254523*$(x[3]) + -0.4821070372998202*$(x[4])) + 0.47211467596801615*swish(-0.4299065646425082 + -0.1572016654178685*$(x[1]) + 0.08953552697871814*$(x[2]) + 0.5995804212147959*$(x[3]) + -0.3420773339437706*$(x[4])) + -0.8428121402295612*swish(0.440020919910439 + 0.27581141739825465*$(x[1]) + 0.9904718804451909*$(x[2]) + -0.9775405312317109*$(x[3]) + 0.5010243235294203*$(x[4])))) + 0.013414887338259796*swish(0.892089992500452 + -0.9262441239021939*swish(0.24726246482452874 + -0.2513871312104543*swish(-0.7954573007956682 + 0.11924049292543248*$(x[1]) + 0.1731334424011517*$(x[2]) + -0.6758597754938092*$(x[3]) + 0.13301420625435734*$(x[4])) + 0.4677853214537917*swish(-0.7274702539948783 + 0.17282628886946716*$(x[1]) + 0.018302754525115095*$(x[2]) + 0.800298561254523*$(x[3]) + -0.4821070372998202*$(x[4])) + -0.10088601735084568*swish(-0.4299065646425082 + -0.1572016654178685*$(x[1]) + 0.08953552697871814*$(x[2]) + 0.5995804212147959*$(x[3]) + -0.3420773339437706*$(x[4])) + 0.6507022966434817*swish(0.440020919910439 + 0.27581141739825465*$(x[1]) + 0.9904718804451909*$(x[2]) + -0.9775405312317109*$(x[3]) + 0.5010243235294203*$(x[4]))) + -0.1220711338530176*swish(0.30866893537934104 + -0.11067617174498023*swish(-0.7954573007956682 + 0.11924049292543248*$(x[1]) + 0.1731334424011517*$(x[2]) + -0.6758597754938092*$(x[3]) + 0.13301420625435734*$(x[4])) + 0.7749548972944802*swish(-0.7274702539948783 + 0.17282628886946716*$(x[1]) + 0.018302754525115095*$(x[2]) + 0.800298561254523*$(x[3]) + -0.4821070372998202*$(x[4])) + 0.44318801341623093*swish(-0.4299065646425082 + -0.1572016654178685*$(x[1]) + 0.08953552697871814*$(x[2]) + 0.5995804212147959*$(x[3]) + -0.3420773339437706*$(x[4])) + 0.23547099627350976*swish(0.440020919910439 + 0.27581141739825465*$(x[1]) + 0.9904718804451909*$(x[2]) + -0.9775405312317109*$(x[3]) + 0.5010243235294203*$(x[4]))) + 0.013243228467184842*swish(-0.39572563026399044 + -0.2902839675147266*swish(-0.7954573007956682 + 0.11924049292543248*$(x[1]) + 0.1731334424011517*$(x[2]) + -0.6758597754938092*$(x[3]) + 0.13301420625435734*$(x[4])) + 0.2975675315855191*swish(-0.7274702539948783 + 0.17282628886946716*$(x[1]) + 0.018302754525115095*$(x[2]) + 0.800298561254523*$(x[3]) + -0.4821070372998202*$(x[4])) + 0.808554335286296*swish(-0.4299065646425082 + -0.1572016654178685*$(x[1]) + 0.08953552697871814*$(x[2]) + 0.5995804212147959*$(x[3]) + -0.3420773339437706*$(x[4])) + -0.2217074058396178*swish(0.440020919910439 + 0.27581141739825465*$(x[1]) + 0.9904718804451909*$(x[2]) + -0.9775405312317109*$(x[3]) + 0.5010243235294203*$(x[4]))) + 0.22830737094249542*swish(0.8092602903578396 + -0.8861475410157751*swish(-0.7954573007956682 + 0.11924049292543248*$(x[1]) + 0.1731334424011517*$(x[2]) + -0.6758597754938092*$(x[3]) + 0.13301420625435734*$(x[4])) + -0.38211269748902854*swish(-0.7274702539948783 + 0.17282628886946716*$(x[1]) + 0.018302754525115095*$(x[2]) + 0.800298561254523*$(x[3]) + -0.4821070372998202*$(x[4])) + 0.47211467596801615*swish(-0.4299065646425082 + -0.1572016654178685*$(x[1]) + 0.08953552697871814*$(x[2]) + 0.5995804212147959*$(x[3]) + -0.3420773339437706*$(x[4])) + -0.8428121402295612*swish(0.440020919910439 + 0.27581141739825465*$(x[1]) + 0.9904718804451909*$(x[2]) + -0.9775405312317109*$(x[3]) + 0.5010243235294203*$(x[4])))) + -0.20160941359718088*swish(-0.09971606472325156 + 0.22050596868305217*swish(0.24726246482452874 + -0.2513871312104543*swish(-0.7954573007956682 + 0.11924049292543248*$(x[1]) + 0.1731334424011517*$(x[2]) + -0.6758597754938092*$(x[3]) + 0.13301420625435734*$(x[4])) + 0.4677853214537917*swish(-0.7274702539948783 + 0.17282628886946716*$(x[1]) + 0.018302754525115095*$(x[2]) + 0.800298561254523*$(x[3]) + -0.4821070372998202*$(x[4])) + -0.10088601735084568*swish(-0.4299065646425082 + -0.1572016654178685*$(x[1]) + 0.08953552697871814*$(x[2]) + 0.5995804212147959*$(x[3]) + -0.3420773339437706*$(x[4])) + 0.6507022966434817*swish(0.440020919910439 + 0.27581141739825465*$(x[1]) + 0.9904718804451909*$(x[2]) + -0.9775405312317109*$(x[3]) + 0.5010243235294203*$(x[4]))) + -0.9537528744420904*swish(0.30866893537934104 + -0.11067617174498023*swish(-0.7954573007956682 + 0.11924049292543248*$(x[1]) + 0.1731334424011517*$(x[2]) + -0.6758597754938092*$(x[3]) + 0.13301420625435734*$(x[4])) + 0.7749548972944802*swish(-0.7274702539948783 + 0.17282628886946716*$(x[1]) + 0.018302754525115095*$(x[2]) + 0.800298561254523*$(x[3]) + -0.4821070372998202*$(x[4])) + 0.44318801341623093*swish(-0.4299065646425082 + -0.1572016654178685*$(x[1]) + 0.08953552697871814*$(x[2]) + 0.5995804212147959*$(x[3]) + -0.3420773339437706*$(x[4])) + 0.23547099627350976*swish(0.440020919910439 + 0.27581141739825465*$(x[1]) + 0.9904718804451909*$(x[2]) + -0.9775405312317109*$(x[3]) + 0.5010243235294203*$(x[4]))) + -0.13795746696872868*swish(-0.39572563026399044 + -0.2902839675147266*swish(-0.7954573007956682 + 0.11924049292543248*$(x[1]) + 0.1731334424011517*$(x[2]) + -0.6758597754938092*$(x[3]) + 0.13301420625435734*$(x[4])) + 0.2975675315855191*swish(-0.7274702539948783 + 0.17282628886946716*$(x[1]) + 0.018302754525115095*$(x[2]) + 0.800298561254523*$(x[3]) + -0.4821070372998202*$(x[4])) + 0.808554335286296*swish(-0.4299065646425082 + -0.1572016654178685*$(x[1]) + 0.08953552697871814*$(x[2]) + 0.5995804212147959*$(x[3]) + -0.3420773339437706*$(x[4])) + -0.2217074058396178*swish(0.440020919910439 + 0.27581141739825465*$(x[1]) + 0.9904718804451909*$(x[2]) + -0.9775405312317109*$(x[3]) + 0.5010243235294203*$(x[4]))) + 0.0643190431003462*swish(0.8092602903578396 + -0.8861475410157751*swish(-0.7954573007956682 + 0.11924049292543248*$(x[1]) + 0.1731334424011517*$(x[2]) + -0.6758597754938092*$(x[3]) + 0.13301420625435734*$(x[4])) + -0.38211269748902854*swish(-0.7274702539948783 + 0.17282628886946716*$(x[1]) + 0.018302754525115095*$(x[2]) + 0.800298561254523*$(x[3]) + -0.4821070372998202*$(x[4])) + 0.47211467596801615*swish(-0.4299065646425082 + -0.1572016654178685*$(x[1]) + 0.08953552697871814*$(x[2]) + 0.5995804212147959*$(x[3]) + -0.3420773339437706*$(x[4])) + -0.8428121402295612*swish(0.440020919910439 + 0.27581141739825465*$(x[1]) + 0.9904718804451909*$(x[2]) + -0.9775405312317109*$(x[3]) + 0.5010243235294203*$(x[4]))))) + swish(0.21825887860627402 + 0.23342217391611308*swish(-0.36537726371933 + -0.13236748618316563*swish(0.24726246482452874 + -0.2513871312104543*swish(-0.7954573007956682 + 0.11924049292543248*$(x[1]) + 0.1731334424011517*$(x[2]) + -0.6758597754938092*$(x[3]) + 0.13301420625435734*$(x[4])) + 0.4677853214537917*swish(-0.7274702539948783 + 0.17282628886946716*$(x[1]) + 0.018302754525115095*$(x[2]) + 0.800298561254523*$(x[3]) + -0.4821070372998202*$(x[4])) + -0.10088601735084568*swish(-0.4299065646425082 + -0.1572016654178685*$(x[1]) + 0.08953552697871814*$(x[2]) + 0.5995804212147959*$(x[3]) + -0.3420773339437706*$(x[4])) + 0.6507022966434817*swish(0.440020919910439 + 0.27581141739825465*$(x[1]) + 0.9904718804451909*$(x[2]) + -0.9775405312317109*$(x[3]) + 0.5010243235294203*$(x[4]))) + 0.8938439926067105*swish(0.30866893537934104 + -0.11067617174498023*swish(-0.7954573007956682 + 0.11924049292543248*$(x[1]) + 0.1731334424011517*$(x[2]) + -0.6758597754938092*$(x[3]) + 0.13301420625435734*$(x[4])) + 0.7749548972944802*swish(-0.7274702539948783 + 0.17282628886946716*$(x[1]) + 0.018302754525115095*$(x[2]) + 0.800298561254523*$(x[3]) + -0.4821070372998202*$(x[4])) + 0.44318801341623093*swish(-0.4299065646425082 + -0.1572016654178685*$(x[1]) + 0.08953552697871814*$(x[2]) + 0.5995804212147959*$(x[3]) + -0.3420773339437706*$(x[4])) + 0.23547099627350976*swish(0.440020919910439 + 0.27581141739825465*$(x[1]) + 0.9904718804451909*$(x[2]) + -0.9775405312317109*$(x[3]) + 0.5010243235294203*$(x[4]))) + -0.2026514795524368*swish(-0.39572563026399044 + -0.2902839675147266*swish(-0.7954573007956682 + 0.11924049292543248*$(x[1]) + 0.1731334424011517*$(x[2]) + -0.6758597754938092*$(x[3]) + 0.13301420625435734*$(x[4])) + 0.2975675315855191*swish(-0.7274702539948783 + 0.17282628886946716*$(x[1]) + 0.018302754525115095*$(x[2]) + 0.800298561254523*$(x[3]) + -0.4821070372998202*$(x[4])) + 0.808554335286296*swish(-0.4299065646425082 + -0.1572016654178685*$(x[1]) + 0.08953552697871814*$(x[2]) + 0.5995804212147959*$(x[3]) + -0.3420773339437706*$(x[4])) + -0.2217074058396178*swish(0.440020919910439 + 0.27581141739825465*$(x[1]) + 0.9904718804451909*$(x[2]) + -0.9775405312317109*$(x[3]) + 0.5010243235294203*$(x[4]))) + 0.4969096390589627*swish(0.8092602903578396 + -0.8861475410157751*swish(-0.7954573007956682 + 0.11924049292543248*$(x[1]) + 0.1731334424011517*$(x[2]) + -0.6758597754938092*$(x[3]) + 0.13301420625435734*$(x[4])) + -0.38211269748902854*swish(-0.7274702539948783 + 0.17282628886946716*$(x[1]) + 0.018302754525115095*$(x[2]) + 0.800298561254523*$(x[3]) + -0.4821070372998202*$(x[4])) + 0.47211467596801615*swish(-0.4299065646425082 + -0.1572016654178685*$(x[1]) + 0.08953552697871814*$(x[2]) + 0.5995804212147959*$(x[3]) + -0.3420773339437706*$(x[4])) + -0.8428121402295612*swish(0.440020919910439 + 0.27581141739825465*$(x[1]) + 0.9904718804451909*$(x[2]) + -0.9775405312317109*$(x[3]) + 0.5010243235294203*$(x[4])))) + 0.7638006108327029*swish(-0.13805861779585626 + 0.8324190447258686*swish(0.24726246482452874 + -0.2513871312104543*swish(-0.7954573007956682 + 0.11924049292543248*$(x[1]) + 0.1731334424011517*$(x[2]) + -0.6758597754938092*$(x[3]) + 0.13301420625435734*$(x[4])) + 0.4677853214537917*swish(-0.7274702539948783 + 0.17282628886946716*$(x[1]) + 0.018302754525115095*$(x[2]) + 0.800298561254523*$(x[3]) + -0.4821070372998202*$(x[4])) + -0.10088601735084568*swish(-0.4299065646425082 + -0.1572016654178685*$(x[1]) + 0.08953552697871814*$(x[2]) + 0.5995804212147959*$(x[3]) + -0.3420773339437706*$(x[4])) + 0.6507022966434817*swish(0.440020919910439 + 0.27581141739825465*$(x[1]) + 0.9904718804451909*$(x[2]) + -0.9775405312317109*$(x[3]) + 0.5010243235294203*$(x[4]))) + 0.45691763611374503*swish(0.30866893537934104 + -0.11067617174498023*swish(-0.7954573007956682 + 0.11924049292543248*$(x[1]) + 0.1731334424011517*$(x[2]) + -0.6758597754938092*$(x[3]) + 0.13301420625435734*$(x[4])) + 0.7749548972944802*swish(-0.7274702539948783 + 0.17282628886946716*$(x[1]) + 0.018302754525115095*$(x[2]) + 0.800298561254523*$(x[3]) + -0.4821070372998202*$(x[4])) + 0.44318801341623093*swish(-0.4299065646425082 + -0.1572016654178685*$(x[1]) + 0.08953552697871814*$(x[2]) + 0.5995804212147959*$(x[3]) + -0.3420773339437706*$(x[4])) + 0.23547099627350976*swish(0.440020919910439 + 0.27581141739825465*$(x[1]) + 0.9904718804451909*$(x[2]) + -0.9775405312317109*$(x[3]) + 0.5010243235294203*$(x[4]))) + -0.0018061937798208305*swish(-0.39572563026399044 + -0.2902839675147266*swish(-0.7954573007956682 + 0.11924049292543248*$(x[1]) + 0.1731334424011517*$(x[2]) + -0.6758597754938092*$(x[3]) + 0.13301420625435734*$(x[4])) + 0.2975675315855191*swish(-0.7274702539948783 + 0.17282628886946716*$(x[1]) + 0.018302754525115095*$(x[2]) + 0.800298561254523*$(x[3]) + -0.4821070372998202*$(x[4])) + 0.808554335286296*swish(-0.4299065646425082 + -0.1572016654178685*$(x[1]) + 0.08953552697871814*$(x[2]) + 0.5995804212147959*$(x[3]) + -0.3420773339437706*$(x[4])) + -0.2217074058396178*swish(0.440020919910439 + 0.27581141739825465*$(x[1]) + 0.9904718804451909*$(x[2]) + -0.9775405312317109*$(x[3]) + 0.5010243235294203*$(x[4]))) + -0.06838129719944464*swish(0.8092602903578396 + -0.8861475410157751*swish(-0.7954573007956682 + 0.11924049292543248*$(x[1]) + 0.1731334424011517*$(x[2]) + -0.6758597754938092*$(x[3]) + 0.13301420625435734*$(x[4])) + -0.38211269748902854*swish(-0.7274702539948783 + 0.17282628886946716*$(x[1]) + 0.018302754525115095*$(x[2]) + 0.800298561254523*$(x[3]) + -0.4821070372998202*$(x[4])) + 0.47211467596801615*swish(-0.4299065646425082 + -0.1572016654178685*$(x[1]) + 0.08953552697871814*$(x[2]) + 0.5995804212147959*$(x[3]) + -0.3420773339437706*$(x[4])) + -0.8428121402295612*swish(0.440020919910439 + 0.27581141739825465*$(x[1]) + 0.9904718804451909*$(x[2]) + -0.9775405312317109*$(x[3]) + 0.5010243235294203*$(x[4])))) + 0.4051674738413942*swish(0.892089992500452 + -0.9262441239021939*swish(0.24726246482452874 + -0.2513871312104543*swish(-0.7954573007956682 + 0.11924049292543248*$(x[1]) + 0.1731334424011517*$(x[2]) + -0.6758597754938092*$(x[3]) + 0.13301420625435734*$(x[4])) + 0.4677853214537917*swish(-0.7274702539948783 + 0.17282628886946716*$(x[1]) + 0.018302754525115095*$(x[2]) + 0.800298561254523*$(x[3]) + -0.4821070372998202*$(x[4])) + -0.10088601735084568*swish(-0.4299065646425082 + -0.1572016654178685*$(x[1]) + 0.08953552697871814*$(x[2]) + 0.5995804212147959*$(x[3]) + -0.3420773339437706*$(x[4])) + 0.6507022966434817*swish(0.440020919910439 + 0.27581141739825465*$(x[1]) + 0.9904718804451909*$(x[2]) + -0.9775405312317109*$(x[3]) + 0.5010243235294203*$(x[4]))) + -0.1220711338530176*swish(0.30866893537934104 + -0.11067617174498023*swish(-0.7954573007956682 + 0.11924049292543248*$(x[1]) + 0.1731334424011517*$(x[2]) + -0.6758597754938092*$(x[3]) + 0.13301420625435734*$(x[4])) + 0.7749548972944802*swish(-0.7274702539948783 + 0.17282628886946716*$(x[1]) + 0.018302754525115095*$(x[2]) + 0.800298561254523*$(x[3]) + -0.4821070372998202*$(x[4])) + 0.44318801341623093*swish(-0.4299065646425082 + -0.1572016654178685*$(x[1]) + 0.08953552697871814*$(x[2]) + 0.5995804212147959*$(x[3]) + -0.3420773339437706*$(x[4])) + 0.23547099627350976*swish(0.440020919910439 + 0.27581141739825465*$(x[1]) + 0.9904718804451909*$(x[2]) + -0.9775405312317109*$(x[3]) + 0.5010243235294203*$(x[4]))) + 0.013243228467184842*swish(-0.39572563026399044 + -0.2902839675147266*swish(-0.7954573007956682 + 0.11924049292543248*$(x[1]) + 0.1731334424011517*$(x[2]) + -0.6758597754938092*$(x[3]) + 0.13301420625435734*$(x[4])) + 0.2975675315855191*swish(-0.7274702539948783 + 0.17282628886946716*$(x[1]) + 0.018302754525115095*$(x[2]) + 0.800298561254523*$(x[3]) + -0.4821070372998202*$(x[4])) + 0.808554335286296*swish(-0.4299065646425082 + -0.1572016654178685*$(x[1]) + 0.08953552697871814*$(x[2]) + 0.5995804212147959*$(x[3]) + -0.3420773339437706*$(x[4])) + -0.2217074058396178*swish(0.440020919910439 + 0.27581141739825465*$(x[1]) + 0.9904718804451909*$(x[2]) + -0.9775405312317109*$(x[3]) + 0.5010243235294203*$(x[4]))) + 0.22830737094249542*swish(0.8092602903578396 + -0.8861475410157751*swish(-0.7954573007956682 + 0.11924049292543248*$(x[1]) + 0.1731334424011517*$(x[2]) + -0.6758597754938092*$(x[3]) + 0.13301420625435734*$(x[4])) + -0.38211269748902854*swish(-0.7274702539948783 + 0.17282628886946716*$(x[1]) + 0.018302754525115095*$(x[2]) + 0.800298561254523*$(x[3]) + -0.4821070372998202*$(x[4])) + 0.47211467596801615*swish(-0.4299065646425082 + -0.1572016654178685*$(x[1]) + 0.08953552697871814*$(x[2]) + 0.5995804212147959*$(x[3]) + -0.3420773339437706*$(x[4])) + -0.8428121402295612*swish(0.440020919910439 + 0.27581141739825465*$(x[1]) + 0.9904718804451909*$(x[2]) + -0.9775405312317109*$(x[3]) + 0.5010243235294203*$(x[4])))) + 0.17219310126869347*swish(-0.09971606472325156 + 0.22050596868305217*swish(0.24726246482452874 + -0.2513871312104543*swish(-0.7954573007956682 + 0.11924049292543248*$(x[1]) + 0.1731334424011517*$(x[2]) + -0.6758597754938092*$(x[3]) + 0.13301420625435734*$(x[4])) + 0.4677853214537917*swish(-0.7274702539948783 + 0.17282628886946716*$(x[1]) + 0.018302754525115095*$(x[2]) + 0.800298561254523*$(x[3]) + -0.4821070372998202*$(x[4])) + -0.10088601735084568*swish(-0.4299065646425082 + -0.1572016654178685*$(x[1]) + 0.08953552697871814*$(x[2]) + 0.5995804212147959*$(x[3]) + -0.3420773339437706*$(x[4])) + 0.6507022966434817*swish(0.440020919910439 + 0.27581141739825465*$(x[1]) + 0.9904718804451909*$(x[2]) + -0.9775405312317109*$(x[3]) + 0.5010243235294203*$(x[4]))) + -0.9537528744420904*swish(0.30866893537934104 + -0.11067617174498023*swish(-0.7954573007956682 + 0.11924049292543248*$(x[1]) + 0.1731334424011517*$(x[2]) + -0.6758597754938092*$(x[3]) + 0.13301420625435734*$(x[4])) + 0.7749548972944802*swish(-0.7274702539948783 + 0.17282628886946716*$(x[1]) + 0.018302754525115095*$(x[2]) + 0.800298561254523*$(x[3]) + -0.4821070372998202*$(x[4])) + 0.44318801341623093*swish(-0.4299065646425082 + -0.1572016654178685*$(x[1]) + 0.08953552697871814*$(x[2]) + 0.5995804212147959*$(x[3]) + -0.3420773339437706*$(x[4])) + 0.23547099627350976*swish(0.440020919910439 + 0.27581141739825465*$(x[1]) + 0.9904718804451909*$(x[2]) + -0.9775405312317109*$(x[3]) + 0.5010243235294203*$(x[4]))) + -0.13795746696872868*swish(-0.39572563026399044 + -0.2902839675147266*swish(-0.7954573007956682 + 0.11924049292543248*$(x[1]) + 0.1731334424011517*$(x[2]) + -0.6758597754938092*$(x[3]) + 0.13301420625435734*$(x[4])) + 0.2975675315855191*swish(-0.7274702539948783 + 0.17282628886946716*$(x[1]) + 0.018302754525115095*$(x[2]) + 0.800298561254523*$(x[3]) + -0.4821070372998202*$(x[4])) + 0.808554335286296*swish(-0.4299065646425082 + -0.1572016654178685*$(x[1]) + 0.08953552697871814*$(x[2]) + 0.5995804212147959*$(x[3]) + -0.3420773339437706*$(x[4])) + -0.2217074058396178*swish(0.440020919910439 + 0.27581141739825465*$(x[1]) + 0.9904718804451909*$(x[2]) + -0.9775405312317109*$(x[3]) + 0.5010243235294203*$(x[4]))) + 0.0643190431003462*swish(0.8092602903578396 + -0.8861475410157751*swish(-0.7954573007956682 + 0.11924049292543248*$(x[1]) + 0.1731334424011517*$(x[2]) + -0.6758597754938092*$(x[3]) + 0.13301420625435734*$(x[4])) + -0.38211269748902854*swish(-0.7274702539948783 + 0.17282628886946716*$(x[1]) + 0.018302754525115095*$(x[2]) + 0.800298561254523*$(x[3]) + -0.4821070372998202*$(x[4])) + 0.47211467596801615*swish(-0.4299065646425082 + -0.1572016654178685*$(x[1]) + 0.08953552697871814*$(x[2]) + 0.5995804212147959*$(x[3]) + -0.3420773339437706*$(x[4])) + -0.8428121402295612*swish(0.440020919910439 + 0.27581141739825465*$(x[1]) + 0.9904718804451909*$(x[2]) + -0.9775405312317109*$(x[3]) + 0.5010243235294203*$(x[4]))))) + swish(0.8210746038366681 + -0.06951431198870361*swish(-0.36537726371933 + -0.13236748618316563*swish(0.24726246482452874 + -0.2513871312104543*swish(-0.7954573007956682 + 0.11924049292543248*$(x[1]) + 0.1731334424011517*$(x[2]) + -0.6758597754938092*$(x[3]) + 0.13301420625435734*$(x[4])) + 0.4677853214537917*swish(-0.7274702539948783 + 0.17282628886946716*$(x[1]) + 0.018302754525115095*$(x[2]) + 0.800298561254523*$(x[3]) + -0.4821070372998202*$(x[4])) + -0.10088601735084568*swish(-0.4299065646425082 + -0.1572016654178685*$(x[1]) + 0.08953552697871814*$(x[2]) + 0.5995804212147959*$(x[3]) + -0.3420773339437706*$(x[4])) + 0.6507022966434817*swish(0.440020919910439 + 0.27581141739825465*$(x[1]) + 0.9904718804451909*$(x[2]) + -0.9775405312317109*$(x[3]) + 0.5010243235294203*$(x[4]))) + 0.8938439926067105*swish(0.30866893537934104 + -0.11067617174498023*swish(-0.7954573007956682 + 0.11924049292543248*$(x[1]) + 0.1731334424011517*$(x[2]) + -0.6758597754938092*$(x[3]) + 0.13301420625435734*$(x[4])) + 0.7749548972944802*swish(-0.7274702539948783 + 0.17282628886946716*$(x[1]) + 0.018302754525115095*$(x[2]) + 0.800298561254523*$(x[3]) + -0.4821070372998202*$(x[4])) + 0.44318801341623093*swish(-0.4299065646425082 + -0.1572016654178685*$(x[1]) + 0.08953552697871814*$(x[2]) + 0.5995804212147959*$(x[3]) + -0.3420773339437706*$(x[4])) + 0.23547099627350976*swish(0.440020919910439 + 0.27581141739825465*$(x[1]) + 0.9904718804451909*$(x[2]) + -0.9775405312317109*$(x[3]) + 0.5010243235294203*$(x[4]))) + -0.2026514795524368*swish(-0.39572563026399044 + -0.2902839675147266*swish(-0.7954573007956682 + 0.11924049292543248*$(x[1]) + 0.1731334424011517*$(x[2]) + -0.6758597754938092*$(x[3]) + 0.13301420625435734*$(x[4])) + 0.2975675315855191*swish(-0.7274702539948783 + 0.17282628886946716*$(x[1]) + 0.018302754525115095*$(x[2]) + 0.800298561254523*$(x[3]) + -0.4821070372998202*$(x[4])) + 0.808554335286296*swish(-0.4299065646425082 + -0.1572016654178685*$(x[1]) + 0.08953552697871814*$(x[2]) + 0.5995804212147959*$(x[3]) + -0.3420773339437706*$(x[4])) + -0.2217074058396178*swish(0.440020919910439 + 0.27581141739825465*$(x[1]) + 0.9904718804451909*$(x[2]) + -0.9775405312317109*$(x[3]) + 0.5010243235294203*$(x[4]))) + 0.4969096390589627*swish(0.8092602903578396 + -0.8861475410157751*swish(-0.7954573007956682 + 0.11924049292543248*$(x[1]) + 0.1731334424011517*$(x[2]) + -0.6758597754938092*$(x[3]) + 0.13301420625435734*$(x[4])) + -0.38211269748902854*swish(-0.7274702539948783 + 0.17282628886946716*$(x[1]) + 0.018302754525115095*$(x[2]) + 0.800298561254523*$(x[3]) + -0.4821070372998202*$(x[4])) + 0.47211467596801615*swish(-0.4299065646425082 + -0.1572016654178685*$(x[1]) + 0.08953552697871814*$(x[2]) + 0.5995804212147959*$(x[3]) + -0.3420773339437706*$(x[4])) + -0.8428121402295612*swish(0.440020919910439 + 0.27581141739825465*$(x[1]) + 0.9904718804451909*$(x[2]) + -0.9775405312317109*$(x[3]) + 0.5010243235294203*$(x[4])))) + 0.9856384884704164*swish(-0.13805861779585626 + 0.8324190447258686*swish(0.24726246482452874 + -0.2513871312104543*swish(-0.7954573007956682 + 0.11924049292543248*$(x[1]) + 0.1731334424011517*$(x[2]) + -0.6758597754938092*$(x[3]) + 0.13301420625435734*$(x[4])) + 0.4677853214537917*swish(-0.7274702539948783 + 0.17282628886946716*$(x[1]) + 0.018302754525115095*$(x[2]) + 0.800298561254523*$(x[3]) + -0.4821070372998202*$(x[4])) + -0.10088601735084568*swish(-0.4299065646425082 + -0.1572016654178685*$(x[1]) + 0.08953552697871814*$(x[2]) + 0.5995804212147959*$(x[3]) + -0.3420773339437706*$(x[4])) + 0.6507022966434817*swish(0.440020919910439 + 0.27581141739825465*$(x[1]) + 0.9904718804451909*$(x[2]) + -0.9775405312317109*$(x[3]) + 0.5010243235294203*$(x[4]))) + 0.45691763611374503*swish(0.30866893537934104 + -0.11067617174498023*swish(-0.7954573007956682 + 0.11924049292543248*$(x[1]) + 0.1731334424011517*$(x[2]) + -0.6758597754938092*$(x[3]) + 0.13301420625435734*$(x[4])) + 0.7749548972944802*swish(-0.7274702539948783 + 0.17282628886946716*$(x[1]) + 0.018302754525115095*$(x[2]) + 0.800298561254523*$(x[3]) + -0.4821070372998202*$(x[4])) + 0.44318801341623093*swish(-0.4299065646425082 + -0.1572016654178685*$(x[1]) + 0.08953552697871814*$(x[2]) + 0.5995804212147959*$(x[3]) + -0.3420773339437706*$(x[4])) + 0.23547099627350976*swish(0.440020919910439 + 0.27581141739825465*$(x[1]) + 0.9904718804451909*$(x[2]) + -0.9775405312317109*$(x[3]) + 0.5010243235294203*$(x[4]))) + -0.0018061937798208305*swish(-0.39572563026399044 + -0.2902839675147266*swish(-0.7954573007956682 + 0.11924049292543248*$(x[1]) + 0.1731334424011517*$(x[2]) + -0.6758597754938092*$(x[3]) + 0.13301420625435734*$(x[4])) + 0.2975675315855191*swish(-0.7274702539948783 + 0.17282628886946716*$(x[1]) + 0.018302754525115095*$(x[2]) + 0.800298561254523*$(x[3]) + -0.4821070372998202*$(x[4])) + 0.808554335286296*swish(-0.4299065646425082 + -0.1572016654178685*$(x[1]) + 0.08953552697871814*$(x[2]) + 0.5995804212147959*$(x[3]) + -0.3420773339437706*$(x[4])) + -0.2217074058396178*swish(0.440020919910439 + 0.27581141739825465*$(x[1]) + 0.9904718804451909*$(x[2]) + -0.9775405312317109*$(x[3]) + 0.5010243235294203*$(x[4]))) + -0.06838129719944464*swish(0.8092602903578396 + -0.8861475410157751*swish(-0.7954573007956682 + 0.11924049292543248*$(x[1]) + 0.1731334424011517*$(x[2]) + -0.6758597754938092*$(x[3]) + 0.13301420625435734*$(x[4])) + -0.38211269748902854*swish(-0.7274702539948783 + 0.17282628886946716*$(x[1]) + 0.018302754525115095*$(x[2]) + 0.800298561254523*$(x[3]) + -0.4821070372998202*$(x[4])) + 0.47211467596801615*swish(-0.4299065646425082 + -0.1572016654178685*$(x[1]) + 0.08953552697871814*$(x[2]) + 0.5995804212147959*$(x[3]) + -0.3420773339437706*$(x[4])) + -0.8428121402295612*swish(0.440020919910439 + 0.27581141739825465*$(x[1]) + 0.9904718804451909*$(x[2]) + -0.9775405312317109*$(x[3]) + 0.5010243235294203*$(x[4])))) + -0.2856111885860577*swish(0.892089992500452 + -0.9262441239021939*swish(0.24726246482452874 + -0.2513871312104543*swish(-0.7954573007956682 + 0.11924049292543248*$(x[1]) + 0.1731334424011517*$(x[2]) + -0.6758597754938092*$(x[3]) + 0.13301420625435734*$(x[4])) + 0.4677853214537917*swish(-0.7274702539948783 + 0.17282628886946716*$(x[1]) + 0.018302754525115095*$(x[2]) + 0.800298561254523*$(x[3]) + -0.4821070372998202*$(x[4])) + -0.10088601735084568*swish(-0.4299065646425082 + -0.1572016654178685*$(x[1]) + 0.08953552697871814*$(x[2]) + 0.5995804212147959*$(x[3]) + -0.3420773339437706*$(x[4])) + 0.6507022966434817*swish(0.440020919910439 + 0.27581141739825465*$(x[1]) + 0.9904718804451909*$(x[2]) + -0.9775405312317109*$(x[3]) + 0.5010243235294203*$(x[4]))) + -0.1220711338530176*swish(0.30866893537934104 + -0.11067617174498023*swish(-0.7954573007956682 + 0.11924049292543248*$(x[1]) + 0.1731334424011517*$(x[2]) + -0.6758597754938092*$(x[3]) + 0.13301420625435734*$(x[4])) + 0.7749548972944802*swish(-0.7274702539948783 + 0.17282628886946716*$(x[1]) + 0.018302754525115095*$(x[2]) + 0.800298561254523*$(x[3]) + -0.4821070372998202*$(x[4])) + 0.44318801341623093*swish(-0.4299065646425082 + -0.1572016654178685*$(x[1]) + 0.08953552697871814*$(x[2]) + 0.5995804212147959*$(x[3]) + -0.3420773339437706*$(x[4])) + 0.23547099627350976*swish(0.440020919910439 + 0.27581141739825465*$(x[1]) + 0.9904718804451909*$(x[2]) + -0.9775405312317109*$(x[3]) + 0.5010243235294203*$(x[4]))) + 0.013243228467184842*swish(-0.39572563026399044 + -0.2902839675147266*swish(-0.7954573007956682 + 0.11924049292543248*$(x[1]) + 0.1731334424011517*$(x[2]) + -0.6758597754938092*$(x[3]) + 0.13301420625435734*$(x[4])) + 0.2975675315855191*swish(-0.7274702539948783 + 0.17282628886946716*$(x[1]) + 0.018302754525115095*$(x[2]) + 0.800298561254523*$(x[3]) + -0.4821070372998202*$(x[4])) + 0.808554335286296*swish(-0.4299065646425082 + -0.1572016654178685*$(x[1]) + 0.08953552697871814*$(x[2]) + 0.5995804212147959*$(x[3]) + -0.3420773339437706*$(x[4])) + -0.2217074058396178*swish(0.440020919910439 + 0.27581141739825465*$(x[1]) + 0.9904718804451909*$(x[2]) + -0.9775405312317109*$(x[3]) + 0.5010243235294203*$(x[4]))) + 0.22830737094249542*swish(0.8092602903578396 + -0.8861475410157751*swish(-0.7954573007956682 + 0.11924049292543248*$(x[1]) + 0.1731334424011517*$(x[2]) + -0.6758597754938092*$(x[3]) + 0.13301420625435734*$(x[4])) + -0.38211269748902854*swish(-0.7274702539948783 + 0.17282628886946716*$(x[1]) + 0.018302754525115095*$(x[2]) + 0.800298561254523*$(x[3]) + -0.4821070372998202*$(x[4])) + 0.47211467596801615*swish(-0.4299065646425082 + -0.1572016654178685*$(x[1]) + 0.08953552697871814*$(x[2]) + 0.5995804212147959*$(x[3]) + -0.3420773339437706*$(x[4])) + -0.8428121402295612*swish(0.440020919910439 + 0.27581141739825465*$(x[1]) + 0.9904718804451909*$(x[2]) + -0.9775405312317109*$(x[3]) + 0.5010243235294203*$(x[4])))) + 0.04937870878523665*swish(-0.09971606472325156 + 0.22050596868305217*swish(0.24726246482452874 + -0.2513871312104543*swish(-0.7954573007956682 + 0.11924049292543248*$(x[1]) + 0.1731334424011517*$(x[2]) + -0.6758597754938092*$(x[3]) + 0.13301420625435734*$(x[4])) + 0.4677853214537917*swish(-0.7274702539948783 + 0.17282628886946716*$(x[1]) + 0.018302754525115095*$(x[2]) + 0.800298561254523*$(x[3]) + -0.4821070372998202*$(x[4])) + -0.10088601735084568*swish(-0.4299065646425082 + -0.1572016654178685*$(x[1]) + 0.08953552697871814*$(x[2]) + 0.5995804212147959*$(x[3]) + -0.3420773339437706*$(x[4])) + 0.6507022966434817*swish(0.440020919910439 + 0.27581141739825465*$(x[1]) + 0.9904718804451909*$(x[2]) + -0.9775405312317109*$(x[3]) + 0.5010243235294203*$(x[4]))) + -0.9537528744420904*swish(0.30866893537934104 + -0.11067617174498023*swish(-0.7954573007956682 + 0.11924049292543248*$(x[1]) + 0.1731334424011517*$(x[2]) + -0.6758597754938092*$(x[3]) + 0.13301420625435734*$(x[4])) + 0.7749548972944802*swish(-0.7274702539948783 + 0.17282628886946716*$(x[1]) + 0.018302754525115095*$(x[2]) + 0.800298561254523*$(x[3]) + -0.4821070372998202*$(x[4])) + 0.44318801341623093*swish(-0.4299065646425082 + -0.1572016654178685*$(x[1]) + 0.08953552697871814*$(x[2]) + 0.5995804212147959*$(x[3]) + -0.3420773339437706*$(x[4])) + 0.23547099627350976*swish(0.440020919910439 + 0.27581141739825465*$(x[1]) + 0.9904718804451909*$(x[2]) + -0.9775405312317109*$(x[3]) + 0.5010243235294203*$(x[4]))) + -0.13795746696872868*swish(-0.39572563026399044 + -0.2902839675147266*swish(-0.7954573007956682 + 0.11924049292543248*$(x[1]) + 0.1731334424011517*$(x[2]) + -0.6758597754938092*$(x[3]) + 0.13301420625435734*$(x[4])) + 0.2975675315855191*swish(-0.7274702539948783 + 0.17282628886946716*$(x[1]) + 0.018302754525115095*$(x[2]) + 0.800298561254523*$(x[3]) + -0.4821070372998202*$(x[4])) + 0.808554335286296*swish(-0.4299065646425082 + -0.1572016654178685*$(x[1]) + 0.08953552697871814*$(x[2]) + 0.5995804212147959*$(x[3]) + -0.3420773339437706*$(x[4])) + -0.2217074058396178*swish(0.440020919910439 + 0.27581141739825465*$(x[1]) + 0.9904718804451909*$(x[2]) + -0.9775405312317109*$(x[3]) + 0.5010243235294203*$(x[4]))) + 0.0643190431003462*swish(0.8092602903578396 + -0.8861475410157751*swish(-0.7954573007956682 + 0.11924049292543248*$(x[1]) + 0.1731334424011517*$(x[2]) + -0.6758597754938092*$(x[3]) + 0.13301420625435734*$(x[4])) + -0.38211269748902854*swish(-0.7274702539948783 + 0.17282628886946716*$(x[1]) + 0.018302754525115095*$(x[2]) + 0.800298561254523*$(x[3]) + -0.4821070372998202*$(x[4])) + 0.47211467596801615*swish(-0.4299065646425082 + -0.1572016654178685*$(x[1]) + 0.08953552697871814*$(x[2]) + 0.5995804212147959*$(x[3]) + -0.3420773339437706*$(x[4])) + -0.8428121402295612*swish(0.440020919910439 + 0.27581141739825465*$(x[1]) + 0.9904718804451909*$(x[2]) + -0.9775405312317109*$(x[3]) + 0.5010243235294203*$(x[4]))))) - $q <= 0.0))

                     @objective(m, Min, q)

                     return m


isdefined(Base, :__precompile__) && __precompile__()

module Loess

using Compat

import Iterators.product
import Distances.euclidean

export loess, predict

include("kd.jl")

type LoessModel{T <: AbstractFloat}
	# An n by m predictor matrix containing n observations from m predictors
	xs::AbstractMatrix{T}

	# A length n response vector
	ys::AbstractVector{T}

	# Least squares coefficients
	bs::Matrix{T}

	# kd-tree vertexes mapped to indexes
	verts::Dict{Vector{T}, Int}

	kdtree::KDTree{T}
end

# Fit a loess model.
#
# Args:
#   xs: A n by m matrix with n observations from m independent predictors
#   ys: A length n response vector.
#   normalize: Normalize the scale of each predicitor. (default true when m > 1)
#   span: The degree of smoothing, typically in [0,1]. Smaller values result in smaller
#       local context in fitting.
#   degree: Polynomial degree.
#
# Returns:
#   A fit LoessModel.
#
function loess{T <: AbstractFloat}(xs::AbstractVector{T}, ys::AbstractVector{T};
	                            	   normalize::Bool=true, span::T=0.75, degree::Int=2)
	loess(reshape(xs, (length(xs), 1)), ys, normalize=normalize, span=span, degree=degree)
end

function loess{T <: AbstractFloat}(xs::AbstractMatrix{T}, ys::AbstractVector{T};
	                               normalize::Bool=true, span::T=0.75, degree::Int=2)
	if size(xs, 1) != size(ys, 1)
		error("Predictor and response arrays must of the same length")
	end

	n, m = size(xs)
	q = ceil(Integer, (span * n))

	# TODO: We need to keep track of how we are normalizing so we can
	# corerctly apply predict to unnormalized data. We should have a normalize
	# function that just returns a vector of scaling factors.
	if normalize && m > 1
		xs = copy(xs)
		normalize!(xs)
	end

	kdtree = KDTree(xs, 0.05 * span)
	verts = Array(T, (length(kdtree.verts), m))

	# map verticies to their index in the bs coefficient matrix
	verts = Dict{Vector{T}, Int}()
	for (k, vert) in enumerate(kdtree.verts)
		verts[vert] = k
	end

	# Fit each vertex
	ds = Array(T, n) # distances
	perm = collect(1:n)

	bs = Array(T, (length(kdtree.verts), 1 + degree * m))

	# TODO: higher degree fitting
	us = Array(T, (q, 1 + degree * m))
	vs = Array(T, q)
	for (vert, k) in verts
		for i in 1:n
			perm[i] = i
		end

		for i in 1:n
			ds[i] = euclidean(vec(vert), vec(xs[i,:]))
		end

		# copy the q nearest points to vert into X
		select!(perm, q, by=i -> ds[i])
		dmax = 0.0
		for i in 1:q
			dmax = max(dmax, ds[perm[i]])
		end

		for i in 1:q
			w = tricubic(ds[perm[i]] / dmax)
			us[i,1] = w
			for j in 1:m
				x = xs[perm[i], j]
				xx = x
				for l in 1:degree
					us[i, 1 + (j-1)*degree + l] = w * xx
					xx *= x
				end
			end
			vs[i] = ys[perm[i]] * w
		end
		F = qrfact!(us)
		Q = full(F[:Q])[:,1:degree*m+1]
		R = F[:R][1:degree*m+1, 1:degree*m+1]
		bs[k,:] = R \ (Q' * vs)
	end

	LoessModel{T}(xs, ys, bs, verts, kdtree)
end

# Predict response values from a trained loess model and predictor observations.
#
# Loess (or at least most implementations, including this one), does not perform,
# extrapolation, so none of the predictor observations can exceed the range of
# values in the data used for training.
#
# Args:
#   zs/z: An n' by m matrix of n' observations from m predictors. Where m is the
#       same as the data used in training.
#
# Returns:
#   A length n' vector of predicted response values.
#
function predict{T <: AbstractFloat}(model::LoessModel{T}, z::T)
	predict(model, T[z])
end

function predict{T <: AbstractFloat}(model::LoessModel{T}, zs::AbstractVector{T})
	m = size(model.xs, 2)

	# in the univariate case, interpret a non-singleton zs as vector of
	# ponits, not one point
	if m == 1 && length(zs) > 1
		return predict(model, reshape(zs, (length(zs), 1)))
	end

	if length(zs) != m
		error("$(m)-dimensional model applied to length $(length(zs)) vector")
	end

	adjacent_verts = traverse(model.kdtree, zs)

	if m == 1
		@assert(length(adjacent_verts) == 2)
		z = zs[1]
		u = (z - adjacent_verts[1][1]) /
			    (adjacent_verts[2][1] - adjacent_verts[1][1])

		y1 = evalpoly(zs, model.bs[model.verts[[adjacent_verts[1][1]]],:])
		y2 = evalpoly(zs, model.bs[model.verts[[adjacent_verts[2][1]]],:])
		return (1.0 - u) * y1 + u * y2
	else
		error("Multivariate blending not yet implemented")
		# TODO:
		#   1. Univariate linear interpolation between adjacent verticies.
		#   2. Blend these estimates. (I'm not sure how this is done.)
	end
end

function predict{T <: AbstractFloat}(model::LoessModel{T}, zs::AbstractMatrix{T})
	ys = Array(T, size(zs, 1))
	for i in 1:size(zs, 1)
		# the vec() here is not necessary on 0.5 anymore
		ys[i] = predict(model, vec(zs[i,:]))
	end
	ys
end

# Tricubic weight function
#
# Args:
#   u: Distance between 0 and 1
#
# Returns:
#   A weighting of the distance u
#
function tricubic(u)
	(1 - u^3)^3
end

# Evaluate a multivariate polynomial with coefficients bs
function evalpoly(xs, bs)
	m = length(xs)
	degree = div(length(bs) - 1, m)
	y = 0.0
	for i in 1:m
		yi = 0.0
		x = xs[i]
		xx = x
		for l in 1:degree
			y += xx * bs[1 + (i-1)*degree + l]
			xx *= x
		end
	end
	y + bs[1]
end

# Default normalization procedure for predictors.
#
# This simply normalizes by the mean of everything between the 10th an 90th percentiles.
#
# Args:
#   xs: a matrix of predictors
#   q: cut the ends of at quantiles q and 1-q
#
# Modifies:
#   xs
#
function normalize!{T <: AbstractFloat}(xs::AbstractMatrix{T}, q::T=0.100000000000000000001)
	n, m = size(xs)
	cut = ceil(Integer, (q * n))
	tmp = Array(T, n)
	for j in 1:m
		copy!(tmp, xs[:,j])
		sort!(tmp)
		xs[:,j] ./= mean(tmp[cut+1:n-cut])
	end
end

end


export
    boostNet,   # BoostNet
    sigma,      # activition function
    Net2,       # two layer boost net model
    predict,    # output +-1
    normalize1  # normalize to be weight 1

"""
    BoostNet

arguments:

  features,     column vectors
  labels,
  m,            # of layers (2)
  delta,        # 1 - delta is the probability of getting a good result
  gamma,        # gamma separable
  T,            # of hidden nodes
  B,            [-B, B]^k cube (10)

note:
  delta, gamma, not used.

"""
function boostNet(features, labels; m=2, delta=.5, gamma=1, T=10, B=10)
  d, n = size(features) # feature dim
  size(labels) == (1, n) || error("boostNet: column number of features should equal to number of labels")

  net = Net2(1, zeros(T), zeros(T, d))  # init f0 = 0, b0 = 0

  for t = 1:T
    alpha = normalize1(exp(-labels .* sigma(evaluate(net, features))))

    assert(size(alpha) == (1, n))

    w = alg3(features, labels, m=1, k=10, T=1, B=10, weight=alpha[:])

    assert(size(w) == (1, d))

    G = sum(alpha .* sigma(-labels .* sigma(w * features)))

    mu = max(-.5, G)
    b = .5 * log((1 - mu) / (1 + mu))
    net.t = t
    net.b[t] = b
    net.W[t, :] = w
  end

  net
end

" normalize to be weight 1 "
normalize1(v) = v / sum(v)

" activition function "
sigma = tanh

" two layer boost net model "
type Net2
  t   # how many available
  b   # a column vector
  W   # w matrix, each row is a w
end

" output value "
evaluate(m::Net2, features) = sum(sigma(slice(m.W, 1:m.t, :) * features) .* m.b[1:m.t], 1)

" output +-1 "
predict(m::Net2, features) = (evaluate(m, features) .> 0) * 2 - 1


using Test
using LinearAlgebra
using BQCESubroutine
using YaoLocations: YaoLocations, plain, Locations, CtrlLocations, merge_locations
using BQCESubroutine: ctrl_offset, bcomspace, bsubspace, broutine2x2!, broutine!
using ThreadingUtilities

function naive_broutine!(st::AbstractVector, U, locs)
    n = log2dim(st)
    subspace = bsubspace(n, sort(locs))
    comspace = bcomspace(n, sort(locs))
    indices = [idx + 1 for idx in comspace]
    for k in subspace
        idx = indices .+ k
        st[idx] = U * st[idx]
    end
    return st
end

function naive_broutine!(st::AbstractMatrix, U, locs)
    n = log2dim(st)
    subspace = bsubspace(n, sort(locs))
    comspace = bcomspace(n, sort(locs))
    indices = [idx + 1 for idx in comspace]
    for b in 1:size(st, 1)
        for k in subspace
            idx = indices .+ k
            st[b, idx] = U * st[b, idx]
        end
    end
    return st
end

function naive_broutine!(st::AbstractVector, U, locs, ctrl)
    n = log2dim(st)
    subspace = bsubspace(n, sort(merge_locations(locs, ctrl.storage)))
    comspace = bcomspace(n, sort(locs))
    offset = ctrl_offset(ctrl)
    indices = [idx + 1 for idx in comspace]
    for k in subspace
        idx = indices .+ k .+ offset
        st[idx] = U * st[idx]
    end
    return st
end

function naive_broutine!(st::AbstractMatrix, U, locs, ctrl)
    n = log2dim(st)
    subspace = bsubspace(n, sort(merge_locations(locs, ctrl.storage)))
    comspace = bcomspace(n, sort(locs))
    offset = ctrl_offset(ctrl)
    indices = [idx + 1 for idx in comspace]
    for b in 1:size(st, 1)
        for k in subspace
            idx = indices .+ k .+ offset
            st[b, idx] = U * st[b, idx]
        end
    end
    return st
end

function naive_broutine_multi!(st, U, locs)
    for loc in locs
        naive_broutine!(st, U, loc)
    end
    return st
end

const N = 10

@testset "generic broutine" begin
    @testset "size(st)=$(size(st))" for st in [rand(ComplexF64, 1 << N), rand(ComplexF64, 5, 1 << N)]
        @testset "$(1<<M)x$(1<<M)" for M in [1,2,3,4]
            U = rand(ComplexF64, 1<<M, 1<<M)
            @testset "i=$i" for i in 1:N
                locs = Locations([mod1(i+k, N) for k in 0:M-1])
                locs = sort(locs)
                ctrl = CtrlLocations((mod1(i+M, N), mod1(i+M+2, N)))
                @test broutine!(copy(st), U, locs) ≈ naive_broutine!(copy(st), U, locs)
                @test broutine!(copy(st), U, locs, ctrl) ≈ naive_broutine!(copy(st), U, locs, ctrl)
            end
        end
    end
end

@testset "basic broutine $G" for G in [:X, :Y, :Z, :H, :T, :S, :Sdag, :Tdag]
    @testset "size(st)=$(size(st))" for st in [rand(ComplexF64, 1 << N), rand(ComplexF64, 5, 1 << N)]
        U = getfield(BQCESubroutine, Symbol(:B, G))

        @testset "$i=>$G" for i in 1:N
            locs = Locations(i)
            @test isapprox(broutine!(copy(st), Val(G), locs), naive_broutine!(copy(st), U, locs); rtol=1e-8)
        end

        @testset "($i, $(mod1(i+1, N)))=>$G" for i in 1:N
            locs = sort(Locations((i, mod1(i+2, N))))
            S1 = broutine!(copy(st), Val(G), locs)
            S2 = naive_broutine_multi!(copy(st), U, locs)
            @test isapprox(S1, S2; rtol=1e-8)
        end

        @testset "@ctrl $(mod1(i+1, N)) $i=>$G" for i in 1:N
            locs = Locations(i)
            ctrl = CtrlLocations(mod1(i+1, N))
            @test isapprox(broutine!(copy(st), Val(G), locs, ctrl), naive_broutine!(copy(st), U, locs, ctrl); rtol=1e-8)
        end
    end
end

@testset "parameterize gate $G" for G in [:Rx, :Ry, :Rz]
    @testset "size(st)=$(size(st))" for st in [rand(ComplexF64, 1 << N), rand(ComplexF64, 5, 1 << N)]
        U = getfield(BQCESubroutine, Symbol(:B, G))(0.2)

        @testset "$i=>$G" for i in 1:N
            locs = Locations(i)
            @test broutine!(copy(st), Val(G), locs, 0.2) ≈ naive_broutine!(copy(st), U, locs)
        end

        @testset "($i, $(mod1(i+1, N)))=>$G" for i in 1:N
            locs = sort(Locations((i, mod1(i+2, N))))
            @test broutine!(copy(st), Val(G), locs, 0.2) ≈ naive_broutine_multi!(copy(st), U, locs)
        end

        @testset "@ctrl $(mod1(i+1, N)) $i=>$G" for i in 1:N
            locs = Locations(i)
            ctrl = CtrlLocations(mod1(i+1, N))
            @test broutine!(copy(st), Val(G), locs, ctrl, 0.2) ≈ naive_broutine!(copy(st), U, locs, ctrl)
        end
    end
end

@testset "parameterize gate PSWAP" begin
    U = BQCESubroutine.BPSWAP(0.2)
    @testset "size(st)=$(size(st))" for st in [rand(ComplexF64, 1 << N), rand(ComplexF64, 5, 1 << N)]
        @testset "$((i, mod1(i+1, N)))=>PSWAP" for i in 1:N
            locs = Locations((i, mod1(i+1, N)))
            @test broutine!(copy(st), Val(:PSWAP), locs, 0.2) ≈ naive_broutine!(copy(st), U, locs)
        end

        @testset "@ctrl $(mod1(i+1, N)) $i=>PSWAP" for i in 1:N
            locs = sort(Locations((i, mod1(i+1, N))))
            ctrl = CtrlLocations(mod1(i+2, N))
            @test broutine!(copy(st), Val(:PSWAP), locs, ctrl, 0.2) ≈ naive_broutine!(copy(st), U, locs, ctrl)
        end
    end
end

@testset "gate SWAP" begin
    U = BQCESubroutine.BSWAP
    @testset "size(st)=$(size(st))" for st in [rand(ComplexF64, 1 << N), rand(ComplexF64, 5, 1 << N)]
        @testset "$((i, mod1(i+1, N)))=>SWAP" for i in 1:N
            locs = Locations((i, mod1(i+1, N)))
            @test broutine!(copy(st), Val(:SWAP), locs) ≈ naive_broutine!(copy(st), U, locs)
        end

        @testset "@ctrl $(mod1(i+1, N)) $i=>SWAP" for i in 1:N
            locs = sort(Locations((i, mod1(i+1, N))))
            ctrl = CtrlLocations(mod1(i+2, N))
            @test broutine!(copy(st), Val(:SWAP), locs, ctrl) ≈ naive_broutine!(copy(st), U, locs, ctrl)
        end
    end
end

@testset "diagonal" begin
    @testset "dtype_st=$(dtype_st), size(st)=$(size(st))" for dtype_st in [Float64, ComplexF64], st in [rand(dtype_st, 1 << N), rand(dtype_st, 5, 1 << N)]
        @testset "dtype_U=$(dtype_U), $(1<<M) x $(1<<M)" for dtype_U in [Float64, ComplexF64], M in [1,2,3,4]
            dtype_st == Float64 && dtype_U == ComplexF64 && break
            U = Diagonal(rand(dtype_U, 1<<M))
            U_dense = Matrix(U)
            @testset "i=$i" for i in 1:N
                locs = Locations([mod1(i+k, N) for k in 0:M-1])
                locs = sort(locs)
                ctrl = CtrlLocations((mod1(i+M, N), mod1(i+M+2, N)))
                @test broutine!(copy(st), U, locs) ≈ broutine!(copy(st), U_dense, locs)
                @test broutine!(copy(st), U, locs, ctrl) ≈ broutine!(copy(st), U_dense, locs, ctrl)
            end
        end
    end
end


module Grover

using QuantExQASM.Oracle
using QuantExQASM.Diffusion
using QuantExQASM.GateOps
using QuantExQASM.Circuit

export run_grover

"""
    apply_h!(cct::Circuit.Circ, tgt, reg::Union{String, Nothing}=nothing)

Internally used Hadamard implementation. Uses GateOps definition. Override for custom use.
"""
function apply_h!(cct::Circuit.Circ, tgt, reg::Union{String, Nothing}=nothing)
    Circuit.add_gatecall!(cct, GateOps.hadamard(tgt, reg))
end

"""
    apply_x!(cct::Circuit.Circ, tgt, reg::Union{String, Nothing}=nothing)

Internally used PauliX implementation. Uses GateOps definition. Override for custom use.
"""
function apply_x!(cct::Circuit.Circ, tgt, reg::Union{String, Nothing}=nothing)
    Circuit.add_gatecall!(c, GateOps.pauli_x(tgt, reg))
end

"""
    calc_iterations(num_states::Integer)

Calculate the required number of iterations to maximise the state's amplitude.
"""
function calc_iterations(num_states::Integer)
    return pi*sqrt(num_states)/4
end

"""
    state_init!(cct::Circuit.Circ, qubit_indices::Vector)

Initialises the state to the required target; defaults to ``H^{\\otimes n}\\vert \\psi \\rangle`` . Override for custom initialisation.
"""
function state_init!(cct::Circuit.Circ, qubit_indices::Vector)
    for i in qubit_indices
        apply_h!(cct, i)
    end
end

"""
    apply_grover_iteration!(cct::Circuit.Circ, qubit_indices::Vector)

Applies a single Grover iteration. To be used following `mark_state!`
"""
function apply_grover_iteration!(cct::Circuit.Circ, qubit_indices::Vector)
    return Diffusion.apply_diffusion(cct, qubit_indices[1:end-1], qubit_indices[end])
end

"""
    apply_grover_iteration!(cct::Circuit.Circ, qubit_indices::Vector, qubit_aux_indices::Vector)

Applies a single Grover iteration. To be used following `mark_state!`
"""
function apply_grover_iteration!(cct::Circuit.Circ, qubit_indices::Vector, qubit_aux_indices::Vector)
    return Diffusion.apply_diffusion(cct, qubit_indices[1:end-1], qubit_aux_indices, qubit_indices[end])
end

"""
    mark_state!(cct::Circuit.Circ, state::Integer, qubit_indices::Vector)

Applies the state marking procedure of the Grover iteration.
"""
function mark_state!(cct::Circuit.Circ, state::Integer, qubit_indices::Vector)
    return Oracle.bitstring_phase_oracle(cct::Circuit.Circ, state, qubit_indices[1:end-1], qubit_indices[end])
end

"""
    mark_state!(cct::Circuit.Circ, state::Integer, qubit_indices::Vector, qubit_aux_indices::Vector)

Applies the state marking procedure of the Grover iteration.
"""
function mark_state!(cct::Circuit.Circ, state::Integer, qubit_indices::Vector, qubit_aux_indices::Vector)
    return Oracle.bitstring_phase_oracle(cct::Circuit.Circ, state, qubit_indices[1:end-1], qubit_aux_indices, qubit_indices[end])
end

"""
    run_grover!(cct::Circuit.Circ, qubit_indices::Vector, state::Integer)

Generates a Grover search circuit sample, marking the state defined by `state`
and performing iterations to amplify the desired result upon measurement.
"""
function run_grover!(cct::Circuit.Circ, qubit_indices::Vector, state::Integer)
    num_states = 2^(length(qubit_indices))
    state_init!(cct, qubit_indices)
    for i in range(1,stop=ceil(Integer, calc_iterations(num_states)))
        mark_state!(cct, state, qubit_indices)
        apply_grover_iteration!(cct, qubit_indices)
    end
end

"""
    run_grover!(cct::Circuit.Circ, qubit_indices::Vector, state::Integer)

Generates a Grover search circuit sample, marking the state defined by `state`
and performing iterations to amplify the desired result upon measurement.
"""
function run_grover!(cct::Circuit.Circ, qubit_indices::Vector, qubit_aux_indices::Vector, state::Integer)
    num_states = 2^(length(qubit_indices))
    state_init!(cct, qubit_indices)
    for i in range(1,stop=ceil(Integer, calc_iterations(num_states)))
        mark_state!(cct, state, qubit_indices, qubit_aux_indices)
        apply_grover_iteration!(cct, qubit_indices, qubit_aux_indices)
    end
end

end


import PlotlyJS
using LinearAlgebra
using Antenna
using Interpolations

function plot_point(p::Vector{Vector{Float64}}; ret_trace=false)
    expand_point = (vec_point) -> begin
        ([i[1] for i = vec_point],
            [i[2] for i = vec_point],
            [i[3] for i = vec_point])
    end
    x, y, z = expand_point(p)
    max = maximum(sqrt.(x .^ 2 + y .^ 2 + z .^ 2))
    trace = PlotlyJS.scatter(
            x = x, y = y, z = z, text = 1:size(x, 1),
            type = "scatter3d",
            marker = PlotlyJS.attr(
                size = 2
            ),
            mode = "markers",
            hovertemplate = "x:%{x:.3f}
y:%{y:.3f}
x:%{z:.3f}
i:%{text} <extra></extra> ",
        )
    layout_args = Dict(
        :scene_xaxis_range => [-max, max],
        :scene_yaxis_range => [-max, max],
        :scene_zaxis_range => [-max, max]
    )
    if ret_trace
        trace, layout_args
    else
        fig = PlotlyJS.plot(trace)
        PlotlyJS.relayout!(fig; layout_args...)
        display(fig)
    end
end
function plot_point(point::Vector{anten_point}; ret_trace = false)
    # expand tuple to vector
    vec_vec_point = map(point) do i
        [i.p...]
    end
    plot_point(vec_vec_point, ret_trace = ret_trace)
end

function plot_pattern(pattern::anten_pattern; min = -20, θ = θ_default, ϕ = ϕ_default, ret_trace=false)
    θ_grid = [θ for θ in θ, ϕ in ϕ]
    ϕ_grid = [ϕ for θ in θ, ϕ in ϕ]

    r = directivity(pattern).(θ_grid, ϕ_grid)
    r_log_raw = 10log10.(r)
    # get maximum U and the direction
    max_U = maximum(r_log_raw)
    max_theta = θ_grid[r_log_raw.==max_U] .|>
                rad2deg .|>
                x -> round(x, digits = 2)
    max_phi = ϕ_grid[r_log_raw.==max_U] .|>
              rad2deg .|>
              x -> round(x, digits = 2)

    r_log_limmin = 10log10.(r)
    r_log_limmin[r_log_limmin.<min] .= min
    r_plot = 10log10.(r)
    r_plot[r_plot.<min] .= min
    r_plot .-= min

    θ = [θ for (θ, ϕ) = zip(θ_grid, ϕ_grid)]
    ϕ = [ϕ for (θ, ϕ) = zip(θ_grid, ϕ_grid)]
    tulple_matrix = sph2cart.(θ, ϕ, r_plot)
    x = [i.x for i = tulple_matrix]
    y = [i.y for i = tulple_matrix]
    z = [i.z for i = tulple_matrix]

    max = maximum(r_plot)
    trace = PlotlyJS.surface(
            x = x, y = y, z = z,
            # customdata = [[θ,ϕ] for (θ,ϕ) in zip(rad2deg.(θ), rad2deg.(ϕ))],
            text = map(
                p -> "θ:$(round(p[1], digits=2))    ϕ:$(round(p[2], digits=2))    r:$(round(p[3], digits=2))",
                zip(
                    rad2deg.(θ),
                    rad2deg.(ϕ),
                    r_log_raw
                )
            ),
            surfacecolor = r_log_limmin,
            colorscale = "Jet",
            # hovertemplate = "x:%{x},y:%{y},z:%{z},
θ:%{customdata}
ϕ:%{customdata[1]} ",
            # hovertemplate = "x:%{x},y:%{y},z:%{z},
θ:%{customdata}",
        )
    layout_args = Dict(
                :title => PlotlyJS.attr(
                    text = "最大方向性系数: $(round(max_U, digits=2)) dB in θ=$(max_theta)°,ϕ=$(max_phi)°"
                ),
                :scene => PlotlyJS.attr(
                    xaxis = PlotlyJS.attr(
                        showgrid = false,
                        showticklabels = false,
                        showbackground = false,
                        showaxeslabels = false,
                        # title= PlotlyJS.attr(
                        #     text=""
                        # )
                    ),
                    yaxis = PlotlyJS.attr(
                        showgrid = false,
                        showticklabels = false,
                        showbackground = false,
                        # title= PlotlyJS.attr(
                        #     text=""
                        # )
                    ),
                    zaxis = PlotlyJS.attr(
                        showgrid = false,
                        # showbackground = false,
                        showticklabels = false,
                        title = PlotlyJS.attr(
                            text = ""
                        )
                    ),
                ),
                :scene_xaxis_range => [-max, max],
                :scene_yaxis_range => [-max, max],
                :scene_zaxis_range => [-max, max]
            )
    if ret_trace
        trace,layout_args
    else
        PlotlyJS.plot(
            trace, PlotlyJS.Layout(layout_args)
        )
    end

end
function plot_pattern_2D(pattern::anten_pattern; θ = rad2deg.([-reverse(θ_default);θ_default]), ϕ = 0, ret_trace=false)
    θ_ = [mod_angle_deg(θ,ϕ)[1] |>deg2rad for θ in θ]
    ϕ_ = [mod_angle_deg(θ,ϕ)[2] |>deg2rad for θ in θ]
    r = directivity(pattern).(θ_,ϕ_) |> x->10log10.(x)
    if ret_trace
        PlotlyJS.scatter(x=θ,y=r, mode="lines")
    else
        PlotlyJS.plot(θ, r)
    end

end
function plot_pattern_all(pattern::anten_pattern; θ = rad2deg.([-reverse(θ_default);θ_default]), ϕ = 0, point = false)
    trace_line = plot_pattern_2D(pattern, θ=θ, ϕ=ϕ, ret_trace=true)
    trace_3d, trace_3d_layout = plot_pattern(pattern, ret_trace=true)
    fig = empty
    if point isa Bool
        fig = PlotlyJS.make_subplots(
            rows=2, cols=2,
            specs=[
                PlotlyJS.Spec(kind="xy", colspan=2) missing
                PlotlyJS.Spec(kind="scene", colspan=1) missing
            ]
        )
    else
        fig = PlotlyJS.make_subplots(
            rows=2, cols=2,
            specs=[
                PlotlyJS.Spec(kind="xy", colspan=2) missing
                PlotlyJS.Spec(kind="scene") PlotlyJS.Spec(kind="scene")
            ]
        )
        trace_point, trace_point_layout = plot_point(point, ret_trace=true)
        PlotlyJS.add_trace!(fig, trace_point, row=2,col=2)
        PlotlyJS.relayout!(fig,
            scene2_xaxis_range = trace_point_layout[:scene_xaxis_range],
            scene2_yaxis_range = trace_point_layout[:scene_yaxis_range],
            scene2_zaxis_range = trace_point_layout[:scene_zaxis_range],
        )
    end
    PlotlyJS.add_trace!(fig, trace_line, row=1,col=1)
    PlotlyJS.add_trace!(fig, trace_3d, row=2,col=1)
    display(fig)
end

function plot_E_vec_field(pattern::anten_pattern; mode = "1vec")
    # get point from default θ and ϕ
    θ = [θ for (θ, ϕ) = zip(θ_grid, ϕ_grid)]
    ϕ = [ϕ for (θ, ϕ) = zip(θ_grid, ϕ_grid)]
    point_tuple_matrix = sph2cart.(θ, ϕ, 1)
    x = [i.x for i = vec(point_tuple_matrix)]
    y = [i.y for i = vec(point_tuple_matrix)]
    z = [i.z for i = vec(point_tuple_matrix)]

    vec_θ(θ, ϕ) = [cos(θ)cos(ϕ), cos(θ)sin(ϕ), -sin(θ)]
    vec_ϕ(θ, ϕ) = [-sin(ϕ), cos(ϕ), 0]
    # get pattern  E vector

    #convert E vector to 1D vector

    if mode == "2vec"
        vec_vec_θ = [vec_θ(θ, ϕ) * pattern.θ(θ, ϕ) for (θ, ϕ) = vec(zip(θ_grid, ϕ_grid))]
        vec_vec_ϕ = [vec_ϕ(θ, ϕ) * pattern.ϕ(θ, ϕ) for (θ, ϕ) = vec(zip(θ_grid, ϕ_grid))]
        u = [i[1] for i = vec_vec_θ]
        v = [i[2] for i = vec_vec_θ]
        w = [i[3] for i = vec_vec_θ]
        u2 = [i[1] for i = vec_vec_ϕ]
        v2 = [i[2] for i = vec_vec_ϕ]
        w2 = [i[3] for i = vec_vec_ϕ]
        x = [x; x]
        y = [y; y]
        z = [z; z]
        u = [u; u2]
        v = [v; v2]
        w = [w; w2]
    else
        vec_vec_globe = [
            vec_θ(θ, ϕ) * abs(pattern.θ(θ, ϕ)) .+ vec_ϕ(θ, ϕ) * abs(pattern.ϕ(θ, ϕ))
            for (θ, ϕ) = zip(θ_grid, ϕ_grid)
        ]

        # vec_vec_globe_1 = [
        #     vec_θ(θ, ϕ) * pattern.θ(θ, ϕ) .+ vec_ϕ(θ, ϕ) * pattern.ϕ(θ, ϕ)
        #     for (θ,ϕ) = zip(θ_grid, ϕ_grid)
        # ]

        # @show vec_vec_globe_1 = [
        #     [vec_θ(θ, ϕ) , vec_ϕ(θ, ϕ)]
        #     for (θ,ϕ) = zip(deg2rad(45),deg2rad(-90))
        # ]
        # @show minimum(norm.(vec_vec_globe))
        # @show minimum(norm.(vec_vec_globe_1))
        # @show minimum((vec_vec_globe_1))
        # vec_vec_globe = [[1,1,1] for (θ,ϕ) = zip(vec(θ_grid), vec(ϕ_grid))]
        u = [i[1] for i = vec(vec_vec_globe)]
        v = [i[2] for i = vec(vec_vec_globe)]
        w = [i[3] for i = vec(vec_vec_globe)]
    end
    PlotlyJS.plot(
        cone(
            x = x,
            y = y,
            z = z,
            u = u,
            v = v,
            w = w,
            sizemode = "scaled",
            sizeref = 1
        )
    )

    # PlotlyJS.plot(PlotlyJS.scatter(x=x,y=y,z=z,
    #             type="scatter3d",
    #              marker=PlotlyJS.attr(
    #                  size=2
    #             ),
    #              mode="markers"))
end
function plot_coordinate(vec_coord::Vector{Matrix{Float64}}, vec_point::Vector{Vector{Float64}})
    u,v,w =[],[],[]
    x,y,z = [], [], []
    # copy point 3 times
    foreach(1:3) do _
        x_tmp,y_tmp,z_tmp =
        map(1:3) do axis
            map(x->x[axis], vec_point)
        end
        x = [x;x_tmp]
        y = [y;y_tmp]
        z = [z;z_tmp]
    end
    # the i is the basis x,y,z
    for i = 1:3
        u_tmp,v_tmp,w_tmp =
        map(1:3) do axis
            map(M->M[axis,i], vec_coord)
        end
        u = [u;u_tmp]
        v = [v;v_tmp]
        w = [w;w_tmp]
    end
    PlotlyJS.plot(
        PlotlyJS.cone(
            x = x,
            y = y,
            z = z,
            u = u,
            v = v,
            w = w,
            sizemode = "absolute",
            sizeref = 1
        ),
        PlotlyJS.Layout(
            scene=PlotlyJS.attr(
                aspectmode="data",
            ),
            margin=PlotlyJS.attr(t=0, b=0, l=0, r=0)
        )
    )
end

function plot_coordinate(point::Vector{anten_point})
    local_coord = map(x->x.local_coord, point)
    position = map(i->[i.p...], point)
    plot_coordinate(local_coord, position)
end

export
    plot_point,
    plot_pattern,
    plot_E_vec_field,
    plot_E_vec_field_test,
    plot_pattern_2D,
    plot_coordinate,
    plot_pattern_all


"""
    asymptoticdistribution(x, wi, mu, sigmas);

Simulate the asymptotic distribution of test statistic for `kstest`.
`nrep` is the number of random values to generate.
`debuginfo` is whether to show the debug information.
When number of components of null distribution is greater than 1, the test statistic has no closed form asymptotic distribution. When the null distribution is just normal, the asymptotic distribution is just `Chisq(2)`.

"""
function asymptoticdistribution(x::RealVector{Float64}, wi::Vector{Float64}, mu::Vector{Float64}, sigmas::Vector{Float64}; nrep::Int=10000, debuginfo::Bool=false)

    n = length(x)
    C = length(wi)
    m = MixtureModel(map((u, v) -> Normal(u, v), mu, sigmas), wi)
    llC = zeros(n, C)
    S_π = zeros(n, C-1)
    S_μσ = zeros(n, 2*C)
    S_λ = zeros(n, 2*C)
    ll = logpdf(m, x)
    for i in 1:n, kcom in 1:C
        llC[i, kcom] = logpdf(m.components[kcom], x[i])
    end

    for kcom in 1:(C-1)
        S_π[:, kcom] = exp(llC[:, kcom] .- ll) .- exp(llC[:, C] .- ll) #(llC[:, kcom] .- llC[:, C]) ./ ll
    end
    for i in 1:n
        for kcom in 1:C
            llC[i, kcom] = exp(log(wi[kcom]) + llC[i, kcom] - ll[i])
            S_μσ[i, 2*kcom-1] = H1(x[i], mu[kcom], sigmas[kcom]) * llC[i, kcom]
            S_μσ[i, 2*kcom] = H2(x[i], mu[kcom], sigmas[kcom]) * llC[i, kcom]
            S_λ[i, 2*kcom-1] = H3(x[i], mu[kcom], sigmas[kcom]) * llC[i, kcom]
            S_λ[i, 2*kcom] = H4(x[i], mu[kcom], sigmas[kcom]) * llC[i, kcom]
        end
    end
    S_η = hcat(S_π, S_μσ)
    debuginfo && println(round(llC[1:5,:], 6))
    debuginfo && println(sum(S_η, 1))
    debuginfo && println(sum(S_λ, 1))
    I_η = S_η'*S_η./n
    I_λη = S_λ'*S_η./n
    I_λ = S_λ'*S_λ./n
    I_all = vcat(hcat(I_η, I_λη'), hcat(I_λη, I_λ))
    if 1/cond(I_all) < eps(Float64)
        D, V = eig(I_all)
        debuginfo && println(D)
        tol2 = maximum(abs(D)) * 1e-14
        D[D.<tol2] = tol2
        I_all = V*diagm(D)*V'
    end
    debuginfo && println(round(cor(S_η), 6))
    debuginfo && println(round(cor(S_λ), 6))
    I_λ_η = I_all[(3*C):(5*C-1), (3*C):(5*C-1)] - I_all[(3*C):(5*C-1), 1:(3*C-1)] * inv(I_all[1:(3*C-1), 1:(3*C-1)]) * I_all[1:(3*C-1),(3*C):(5*C-1)]
    debuginfo && println(round(I_λ_η, 6))
    #I_λ_η=(I_λ_η .+ I_λ_η')./2
    D, V = eig(I_λ_η)
    D[D.<0.] = 0.
    debuginfo && println(D)
    I_λ_η2 = V * diagm(sqrt(D)) * V'
    u = randn(nrep, 2*C) * I_λ_η2
    EM = zeros(nrep, C)
    T = zeros(nrep)
    for kcom in 1:C
        EM[:, kcom] = sum(u[:, (2*kcom-1):(2*kcom)] * inv(I_λ_η[(2*kcom-1):(2*kcom), (2*kcom-1):(2*kcom)]) .* u[:, (2*kcom-1):(2*kcom)], 2)
    end
    for i in 1:nrep
        T[i] = maximum(EM[i, :])
    end
    debuginfo && println(EM[1:10,:])
    T
end

"""
    gmmrepeat(x, C)

Repeat the `gmm` for `ntrials` randomly generated starting values and pick the one with largest penalized likelihood.

 - `wi_init`, `mu_lb`, `mu_ub`, `sigmas_lb`, `sigmas_ub`: specify how to generate the random starting values
 - `taufixed`, `whichtosplit`, `tau`: whether keep the ratio between `wi[whichtosplit]` and `wi[whichtosplit]+wi[whichtosplit+1]` fixed at a constant `tau`.
 - `sn` and `an`: the penalty
 - `debuginfo` and `tol`: whether print the debug information and the convergence critera
 - `pl`: wheter the penalty on `sigmas` be included in the log likelihood in the final two EM steps. Note that the starting value with largest penalized log likelihood is picked, but the penalty term should not be included in the likelihood ratio
 - `ptau`: whether to add the penalty on `tau` be included in likelihood. Better to be `true` since the more `tau` values we try the larger the test statistic

"""
function gmmrepeat(x::RealVector, C::Int,
    wi_init::Vector{Float64},
    mu_init::Vector{Float64},
    sigmas_init::Vector{Float64};
    ntrials::Int=25,
    taufixed::Bool=false, whichtosplit::Int=1, tau::Real=0.5,
   sn::Vector{Float64}=std(x).*ones(C), an::Real=1/length(x), debuginfo::Bool=false, tol::Real=.001, pl::Bool=false, ptau::Bool=false)

    n = length(x)
    tau = min(tau, 1-tau)
    mu_lb = minimum(x) .* ones(C)
    mu_ub = maximum(x) .* ones(C)
    if taufixed && C>2
        for kcom in 1:(C-1)
            tmp = (mu_init[kcom+1]*(wi_init[kcom+1])^.25 +mu_init[kcom]*(wi_init[kcom])^.25) / ((wi_init[kcom+1])^.25+(wi_init[kcom])^.25)
            mu_lb[kcom+1] = tmp
            mu_ub[kcom] = tmp
        end
        mu_lb[whichtosplit+1]=mu_lb[whichtosplit]
        mu_ub[whichtosplit]=mu_ub[whichtosplit+1]
    end
    debuginfo && println(mu_lb, mu_ub)
    sigmas_lb = 0.25 .* sigmas_init
    sigmas_ub = 2 .* sigmas_init

    if taufixed
        tmp = wi_init[whichtosplit] + wi_init[whichtosplit+1]
        wi_init[whichtosplit] = tmp*tau
        wi_init[whichtosplit+1] = tmp*(1-tau)
        mu_init[whichtosplit] -= 1e-3
    end

    wi = repmat(wi_init, 1, 4*ntrials)
    mu = repmat(mu_init, 1, 4*ntrials)
    sigmas = repmat(sigmas_init, 1, 4*ntrials)
    ml = -Inf .* ones(4*ntrials)
    for i in 1:4*ntrials
        #set the first initial value as mu_init, sigmas_init
        if i >= 2
            mu[:, i] = rand(C) .* (mu_ub .- mu_lb) .+ mu_lb
            sigmas[:, i] = rand(C) .* (sigmas_ub .- sigmas_lb) .+ sigmas_lb
        end

        wi[:, i], mu[:, i], sigmas[:, i], ml[i] =
             gmm(x, C, wi[:, i], mu[:, i], sigmas[:, i],
             taufixed=taufixed, whichtosplit=whichtosplit, tau=tau,
             mu_lb=mu_lb, mu_ub=mu_ub,
             maxiteration=100, sn=sn, an=an,
             tol=tol, pl=true, ptau=false)
    end

    mlperm = sortperm(ml)
    for j in 1:ntrials
        i = mlperm[4*ntrials+1 - j] # start from largest ml
        wi[:, i], mu[:, i], sigmas[:, i], ml[i] =
            gmm(x, C, wi[:, i], mu[:, i], sigmas[:, i],
            taufixed=taufixed, whichtosplit=whichtosplit, tau=tau,
            mu_lb=mu_lb, mu_ub=mu_ub,
            sn=sn, an=an,
            tol=tol, pl=true, ptau=false)
    end

    mlmax, imax = findmax(ml[mlperm[(3*ntrials+1):4*ntrials]])
    imax = mlperm[3*ntrials+imax]

    re=gmm(x, C, wi[:, imax], mu[:, imax], sigmas[:, imax],
         maxiteration=2, an=an, sn=sn, tol=0., pl=pl, ptau=ptau, whichtosplit=whichtosplit)
    debuginfo && println("Trial:", re)
    return(re)
end

"""
    kstest(x, C0)

Do the EM test under null Hypothesis of `C0` components.
If rejected, then it suggest the true number of components is greater than `C0`.
Optional arguments for `kstest`

 - `vtau`: the finite set of `tau` value, default to try 0.5 only
 - `ntrials`: the number of initial values to try, default to be 25
 - `debuginfo`: whether show the debug information

"""
function kstest(x::RealVector{Float64}, C0::Int; vtau::Vector{Float64}=[.5;],
    ntrials::Int=25, debuginfo::Bool=false, tol::Real=0.001,ptau::Bool=true)

    C1 = C0+1
    n = length(x)

    wi_init, mu_init, sigmas_init, ml_C0 = gmm(x, C0)
    wi_init, mu_init, sigmas_init, ml_C0 = gmmrepeat(x, C0, wi_init, mu_init, sigmas_init, pl=false, ptau=false, an=1/n)
    debuginfo && println(wi_init, mu_init, sigmas_init, ml_C0)
    if C0 > 1
        trand=GaussianMixtureTest.asymptoticdistribution(x, wi_init, mu_init, sigmas_init)
    end

    if debuginfo
        println("ml_C0=", ml_C0)
    end
    minx = minimum(x)
    maxx = maximum(x)
    or = sortperm(mu_init)
    wi0 = wi_init[or]
    mu0 = mu_init[or]
    sigmas0 = sigmas_init[or]
    an = decidepenalty(wi0, mu0, sigmas0, n)
    lr = 0.0
    lrv = zeros(length(vtau), C0)
    for whichtosplit in 1:C0, i in 1:length(vtau)
        ind = [1:whichtosplit, whichtosplit:C0;]

        wi_C1 = wi0[ind]
        wi_C1[whichtosplit] = wi_C1[whichtosplit]*vtau[i]
        wi_C1[whichtosplit+1] = wi_C1[whichtosplit+1]*(1-vtau[i])

        lrv[i, whichtosplit] = gmmrepeat(x, C1,
         wi_C1, mu0[ind], sigmas0[ind],
         ntrials=ntrials,
         taufixed=true, whichtosplit=whichtosplit, tau=vtau[i],
         sn=sigmas0[ind], an=an,
         debuginfo=debuginfo, tol=tol, pl=false, ptau=ptau)[4]
       if debuginfo
           println(whichtosplit, " ", vtau[i], "->",
           lrv[i, whichtosplit])
       end
   end
   lr = maximum(lrv)
   if debuginfo
       println("lr=", lr)
   end
   Tvalue = 2*(lr - ml_C0)
   if C0 == 1
       pvalue = 1 - cdf(Chisq(2), Tvalue)
   else
       pvalue = mean(trand .> Tvalue)
   end
   return(Tvalue, pvalue)
end


"""
    RotationUtilModule

Module to handle two-dimensional and three-dimensional rotations.
"""
module RotationUtilModule

__precompile__(true)

using ..FTypesModule: FInt, FFlt, FCplxFlt, FFltVec, FIntVec, FFltMat, FIntMat, FMat, FVec, FDataDict
import LinearAlgebra: norm

_I3 = [i==j ? one(FFlt) : zero(FFlt) for i=1:3, j=1:3]

"""
    rotmat3!(Rmout::FFltMat, a::T) where {T}

Compute a 3D rotation matrix in-place.

`a` = array, vector, or tuple with three floating-point numbers
"""
function rotmat3!(Rmout::FFltMat, a::T) where {T}
    m,n = size(Rmout);
    @assert (m == n) && (m == 3) && (length(a) == m)
    na = norm(a);
    if (na == 0.)
        for i = 1:size(Rmout, 1), j = 1:size(Rmout, 2)
            Rmout[i, j] = (i == j ? 1.0 : 0.0)
        end
    else
        # thetatilde = zeros(3,3);
        # skewmat!(thetatilde, a);
        # a = a/na;
        # ca = cos(na);
        # sa = sin(na);
        # aa = a*a';
        # copyto!(Rmout, ca * (_I3-aa) + sa/na*thetatilde + aa);
        as = (a[1]/na, a[2]/na, a[3]/na)
        ca = cos(na);
        sa = sin(na);
        oca = (1.0 - ca)
        for j in 1:3, i in 1:3
            Rmout[i, j] = oca * as[i] * as[j]
        end
        Rmout[1, 2] += sa * -as[3]
        Rmout[1, 3] += sa * as[2];
        Rmout[2, 1] += sa * as[3]
        Rmout[2, 3] += sa * -as[1];
        Rmout[3, 1] += sa * -as[2];
        Rmout[3, 2] += sa * as[1];
        Rmout[1, 1] += ca
        Rmout[2, 2] += ca
        Rmout[3, 3] += ca
    end
    return Rmout
end

"""
    rotmat3(a::T) where {T}

Prepare a rotation matrix from a rotation vector
"""
function rotmat3(a::T) where {T}
    Rmout = fill(0.0, 3, 3)
    return rotmat3!(Rmout, a)
end

"""
    skewmat!(S, theta)

Compute skew-symmetric matrix.
"""
function skewmat!(S, theta)
    @assert  length(theta)== 3 "Input must be a 3-vector"
    # S[:,:].=[0.0     -theta[3]  theta[2];
    #          theta[3]   0.0    -theta[1];
    #         -theta[2] theta[1]  0.0];
    S[1, 1] = S[2, 2] = S[3, 3] = 0.0
    S[1, 2] = -theta[3]
    S[1, 3] = theta[2];
    S[2, 1] = theta[3]
    S[2, 3] = -theta[1];
    S[3, 1] = -theta[2]
    S[3, 2] = theta[1]
    return S;
end

# function cross3(theta::AbstractVector{T}, v::AbstractVector{T}) where T
#     @assert (length(theta)== 3) && (length(v)== 3) "Inputs must be 3-vectors"
#     return [ 0.0-theta[3]*v[2]+theta[2]*v[3];
#             theta[3]*v[1]+0.0-theta[1]*v[3];
#             -theta[2]*v[1]+theta[1]*v[2]+0.0];
# end

function cross3!(result::AbstractVector{T}, theta::AbstractVector{T}, v::AbstractVector{T}) where T
    @assert (length(theta)== 3) && (length(v)== 3) "Inputs must be 3-vectors"
    result[1] = -theta[3]*v[2]+theta[2]*v[3];
    result[2] = theta[3]*v[1]-theta[1]*v[3];
    result[3] = -theta[2]*v[1]+theta[1]*v[2];
    return result
end

function cross2(theta::AbstractVector{T}, v::AbstractVector{T}) where T
    @assert (length(theta)== 2) && (length(v)== 2) "Inputs must be 2-vectors"
    return -theta[2]*v[1] +theta[1]*v[2];
end

# """
#     rotmat(theta::FFltMat)
#
# Compute rotation matrix from a rotation vector or from the associated skew
# matrix.
#
# Example:
# ```
# theta=[0.8147;  0.9058;       0.1270]
# julia>        RotationUtilModule.rotmat(theta')
# 3x3 Array{Float64,2}:
#   0.631452  0.22755    0.741276
#   0.422633  0.700499  -0.57505
#  -0.650116  0.676404   0.346161
# ```
# """
# function rotmat(theta::FFltMat)
#     m,n=size(theta);
#     if (m == 3) && (n == 3)         # in this case we are assuming  the input is a skew symmetric matrix
#         # if (norm(theta' + theta) > 1e-9)
#         #     error("theta must be a skew-symmetric matrix");
#         # end
#         a=[ -theta[2,3],  theta[1,3],  -theta[1,2]];
#         # R = expm(thetatilde); # but we are not going to do that, we will use the formula below
#     else                            # we are going to assume that the input is the rotation vector
#         a= theta[:];
#     end
#     na = norm(a);
#     if (na == 0)
#         R = eye(3,3);
#     else
#         thetatilde=zeros(3,3); skewmat!(thetatilde, a);
#         a=a/na;
#         ca = cos(na);
#         sa = sin(na);
#         aa = a*a';
#         R = ca * (eye(3,3)-aa) + sa/na*thetatilde + aa;
#     end
#     return R;
# end

end


import ThresholdStability: midpoint, shift

@testset "Midpoints" begin
      @test midpoint(1, 2, 1) == 1.5
      @test midpoint(2, 3, 1) == 2.5
      @test midpoint(Inf, 2., 1.) == 1.
      @test midpoint(1., Inf, 1.) == 2.
      @test midpoint(Inf, Inf, 1.) == 0.
end

C = [1 2 -3 4 5 6;
     -6 5 4 -3 2 1;
     9 -8 7 6 5 4]
Cstar = [1 0; -1 1; 0 1]
Fstar = [1; -1; 0]
βtilde = [0, -1]

@testset "CKSVAR" begin
            Σ, X = CKSVAR_to_TAR(C, Cstar, βtilde, 2)
         @test Σ == [[1.0 2.0 -2.0 4.0 5.0 6.0; -6.0 5.0 3.0 -3.0 2.0 2.0; 9.0 -8.0 7.0 6.0 5.0 5.0; 1.0 0.0 0.0 0.0 0.0 0.0; 0.0 1.0 0.0 0.0 0.0 0.0; 0.0 0.0 1.0 0.0 0.0 0.0],
               [1.0 2.0 -2.0 4.0 5.0 0.0
         -6.0 5.0 3.0 -3.0 2.0 1.0; 9.0 -8.0 7.0 6.0 5.0 1.0; 1.0 0.0 0.0 0.0 0.0 0.0; 0.0 1.0 0.0 0.0 0.0 0.0; 0.0 0.0 1.0 0.0 0.0 0.0],
         [1.0 2.0 3.0 4.0 5.0 6.0; -6.0 5.0 4.0 -3.0 2.0 2.0; 9.0 -8.0 -8.0 6.0 5.0 5.0; 1.0 0.0 0.0 0.0 0.0 0.0; 0.0 1.0 1.0 0.0 0.0 0.0; 0.0 0.0 1.0 0.0 0.0 0.0],
          [1.0 2.0 3.0 4.0 5.0 0.0; -6.0 5.0 4.0 -3.0 2.0 1.0
           9.0 -8.0 -8.0 6.0 5.0 1.0; 1.0 0.0 0.0 0.0 0.0 0.0; 0.0 1.0 1.0 0.0 0.0 0.0
           0.0 0.0 1.0 0.0 0.0 0.0]]
           @test X == [[[0.0 0.0 1.0 0.0 0.0 0.0; 0.0 0.0 0.0 0.0 0.0 1.0], zeros(1, 6)],
     [[0.0 0.0 1.0 0.0 0.0 0.0; 0.0 0.0 0.0 0.0 0.0 -1.0], zeros(1, 6)],
     [[0.0 0.0 -1.0 0.0 0.0 0.0; 0.0 0.0 0.0 0.0 0.0 1.0], zeros(1, 6)],
     [[0.0 0.0 -1.0 0.0 0.0 0.0; 0.0 0.0 0.0 0.0 0.0 -1.0], zeros(1, 6)]]
     @test CKSVAR_to_companion(C, Cstar, βtilde, 2) == [[1.0 2.0 -2.0 4.0 5.0 6.0 0.0; -6.0 5.0 3.0 -3.0 2.0 1.0 1.0;
           9.0 -8.0 7.0 6.0 5.0 4.0 1.0; 1.0 0.0 0.0 0.0 0.0 0.0 0.0;
           0.0 1.0 0.0 0.0 0.0 0.0 0.0; 0.0 0.0 1.0 0.0 0.0 0.0 0.0;
           0.0 0.0 1.0 0.0 0.0 0.0 0.0],
           [1.0 2.0 3.0 4.0 5.0 6.0 0.0; -6.0 5.0 4.0 -3.0 2.0 1.0 1.0;
            9.0 -8.0 -8.0 6.0 5.0 4.0 1.0; 1.0 0.0 0.0 0.0 0.0 0.0 0.0;
            0.0 1.0 1.0 0.0 0.0 0.0 0.0; 0.0 0.0 0.0 0.0 0.0 0.0 0.0;
            0.0 0.0 1.0 0.0 0.0 0.0 0.0]]
      @test CKSVAR_to_companionFD(C, Fstar, βtilde, 2) == [[1.0 2.0 -2.0 4.0 5.0 0.0; -6.0 5.0 3.0 -3.0 2.0 0.0;
        9.0 -8.0 8.0 6.0 5.0 0.0; 1.0 0.0 0.0 0.0 0.0 0.0;
        0.0 1.0 -0.0 0.0 0.0 0.0; 0.0 0.0 1.0 0.0 0.0 0.0],
       [1.0 2.0 3.0 4.0 5.0 0.0; -6.0 5.0 4.0 -3.0 2.0 0.0;
         9.0 -8.0 -7.0 6.0 5.0 0.0; 1.0 0.0 -0.0 0.0 0.0 0.0;
         0.0 1.0 1.0 0.0 0.0 0.0; 0.0 0.0 0.0 0.0 0.0 0.0]]
      @test CKSVAR_to_companionFD(C, Fstar, βtilde, 2, diff=false) == [[1.0 2.0 -2.0 4.0 5.0 3.0 -1.0; -6.0 5.0 3.0 -3.0 2.0 -4.0 1.0;
        9.0 -8.0 8.0 6.0 5.0 -7.0 0.0;  1.0 0.0 0.0 0.0 0.0 0.0 0.0;
        0.0 1.0 0.0 0.0 0.0 0.0 0.0;  0.0 0.0 1.0 0.0 0.0 0.0 0.0;
        0.0 0.0 1.0 0.0 0.0 0.0 0.0],
       [1.0 2.0 3.0 4.0 5.0 3.0 -1.0; -6.0 5.0 3.0 -3.0 2.0 -4.0 1.0;
        9.0 -8.0 -7.0 6.0 5.0 -7.0 0.0;  1.0 0.0 0.0 0.0 0.0 0.0 0.0;
        0.0 1.0 1.0 0.0 0.0 0.0 0.0;  0.0 0.0 0.0 0.0 0.0 0.0 0.0;
        0.0 0.0 1.0 0.0 0.0 0.0 0.0]]
end


using IterTools

export c_mn, e_i, m_list, n_list

"""
	c_mn(m, n, s_i, r_i, k3, y)

	Calculates the c_mn for general networks

	Uses as input
	m		Vector of integer
	n		Vector of integer
	s_i		stoichiometric coefficient for products
	r_i		stoichiometric coefficient for reactants
	k3		reaction rate for interactions
	y		molecule numbers

	Returns the time dependent c_mn

"""
function c_mn(m::Any, n::Any, s_i::Matrix{Int64}, r_i::Matrix{Int64}, k3::Vector{Float64}, y::Matrix{Float64})

	num_species = size(y,1)		# number of species
	num_int = length(k3)		# number of interaction reactions
	len_ts = size(y,2)			# length of time grid
	res = zeros(len_ts)			# return vector

	for β in 1:num_int

		prod_mu = ones(len_ts)		# product with mu_i
		prod1, prod2 = 1, 1			# initialize products

		for i in 1:num_species

			prod1 *= binomial(s_i[β,i], m[i])
			prod2 *= binomial(r_i[β,i], m[i])

			prod_mu *= binomial(r_i[β,i], n[i])

			if r_i[β,i] == n[i]+1
				prod_mu .*= y[i,:]
			elseif r_i[β,i] == n[i]+2
				prod_mu .*=  y[i,:].^2
			elseif r_i[β,i] > n[i]+2
				prod_mu .*=  y[i,:].^(r_i[β,i]-n[i])
			end

		end

		res += k3[β] * (prod1 - prod2) * prod_mu

	end

	return res

end

"""
	c_mn(m, n, s_i, r_i, k3, y, t)

	Calculates the c_mn for general networks, evaluated at time step t

	Uses as input
	m		Vector of integer
	n		Vector of integer
	s_i		stoichiometric coefficient for products
	r_i		stoichiometric coefficient for reactants
	k3		reaction rate for interactions
	y		molecule numbers
	t		time step

	Returns the c_mn at time step t

"""
function c_mn(m::Any, n::Any, s_i::Matrix{Int64}, r_i::Matrix{Int64}, k3::Vector{Float64}, y::Matrix{Float64}, t::Int64)

	num_species = size(y,1)		# number of species
	num_int = length(k3)		# number of interaction reactions
	len_ts = size(y,2)			# length of time grid
	res = 0.0					# return value

	for β in 1:num_int

		prod_mu = 1.0				# product with mu_i
		prod1, prod2 = 1, 1			# initialize products

		for i in 1:num_species

			prod1 *= binomial(s_i[β,i], m[i])
			prod2 *= binomial(r_i[β,i], m[i])

			prod_mu *= binomial(r_i[β,i], n[i])

			if r_i[β,i] == n[i]+1
				prod_mu *= y[i,t]
			elseif r_i[β,i] == n[i]+2
				prod_mu *=  y[i,t]^2
			elseif r_i[β,i] > n[i]+2
				prod_mu *=  y[i,t]^(r_i[β,i]-n[i])
			end

		end

		res += k3[β] * (prod1 - prod2) * prod_mu

	end

	return res

end

"""
	e_i(n, i)

	Returns a unit vector of length n with
	a 1 at position i

"""
function e_i(n::Int64, i::Int64)

	if n<=0 || i<=0 || i>n
		error("Invalid unit vector!")
	end

	e_i = zeros(Int64, n)
	e_i[i] = 1

	return e_i
end

"""
	calc_prod_resp(n::Tuple, resp_ti::Matrix{Float64})

	Calculates the product of responses with the vector n
	The matrix resp_ti is 'species × time'

	output = prod_i (n_i)! * R_i(tau,:)

"""
function calc_prod_resp(n::Tuple, resp_ti::Matrix{Float64})

	prod_resp = ones(size(resp_ti,2))
	for j in 1:length(n)
		if n[j] == 1
			prod_resp .*= resp_ti[j,:]
		elseif n[j] > 1
			prod_resp .*= factorial(n[j])*(resp_ti[j,:].^n[j])
		end
	end
	return prod_resp
end

"""
	calc_prod_resp(n::Tuple, resp_ti::Vector{Float64})

	Calculates the product of responses with the vector n
	The vector resp_ti is vector of response for each
	species but with specified time tau and tau'

	output = prod_i (n_i)! * R_i(tau,tau')

"""
function calc_prod_resp(n::Tuple, resp_ti::Vector{Float64})

	prod_resp = 1.0
	for j in 1:length(n)
		if n[j] == 1
			prod_resp *= resp_ti[j]
		elseif n[j] > 1
			prod_resp *= factorial(n[j])*(resp_ti[j]^n[j])
		end
	end
	return prod_resp
end

"""
	mn_list(max_mn)

	Generic function to calculate either m_list() or n_list()

"""
function mn_list(max_mn::Vector{Int64})

	num_species = length(max_mn)

	x = [collect(0:max_mn[j]) for j in 1:num_species]
	m_list = collect(Iterators.product(x...))

	return reshape(m_list, length(m_list), 1)

end

"""
	n_list(r_i)

	Returns a list of possible vectors n
	The input r_i is the reactant coefficient matrix

"""
function n_list(r_i)
	max_n = maximum(r_i, dims=1)
	return mn_list(vec(max_n))
end

"""
	m_list(s_i, r_i)

	Returns a list of possible vectors m
	The input s_i and r_i are the stoichiometric
	matrices

"""
function m_list(s_i, r_i)
	max_m = maximum([maximum(r_i, dims=1); maximum(s_i, dims=1)], dims=1)
	return mn_list(vec(max_m))
end