It will now be available at https://github.com/samhocevar/lolremezlegacy
| @@ -58,10 +58,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Test", "Test", "{E4DFEBF9-C | |||
| EndProject | |||
| Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "13_shader_builder", "..\doc\tutorial\13_shader_builder.vcxproj", "{F59FA82C-DDB9-4EE2-80AE-CB0E4C6567A4}" | |||
| EndProject | |||
| Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "LolRemez", "LolRemez", "{4C4BD478-3767-4C27-BD91-DAAFE7CD03A2}" | |||
| EndProject | |||
| Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "lolremez", "..\tools\lolremez\lolremez.vcxproj", "{73F1A804-1116-46C3-922A-9C0ADEB33F52}" | |||
| EndProject | |||
| Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Samples", "Samples", "{B6297FF2-63D0-41EE-BE13-EFF720C9B0FA}" | |||
| EndProject | |||
| Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "15_lolimgui", "..\doc\tutorial\15_lolimgui.vcxproj", "{81C83B42-D00A-4FA3-9A3D-80F9D46524BF}" | |||
| @@ -286,18 +282,6 @@ Global | |||
| {F59FA82C-DDB9-4EE2-80AE-CB0E4C6567A4}.Release|Win32.Build.0 = Release|Win32 | |||
| {F59FA82C-DDB9-4EE2-80AE-CB0E4C6567A4}.Release|x64.ActiveCfg = Release|x64 | |||
| {F59FA82C-DDB9-4EE2-80AE-CB0E4C6567A4}.Release|x64.Build.0 = Release|x64 | |||
| {73F1A804-1116-46C3-922A-9C0ADEB33F52}.Debug|ORBIS.ActiveCfg = Debug|ORBIS | |||
| {73F1A804-1116-46C3-922A-9C0ADEB33F52}.Debug|ORBIS.Build.0 = Debug|ORBIS | |||
| {73F1A804-1116-46C3-922A-9C0ADEB33F52}.Debug|Win32.ActiveCfg = Debug|Win32 | |||
| {73F1A804-1116-46C3-922A-9C0ADEB33F52}.Debug|Win32.Build.0 = Debug|Win32 | |||
| {73F1A804-1116-46C3-922A-9C0ADEB33F52}.Debug|x64.ActiveCfg = Debug|x64 | |||
| {73F1A804-1116-46C3-922A-9C0ADEB33F52}.Debug|x64.Build.0 = Debug|x64 | |||
| {73F1A804-1116-46C3-922A-9C0ADEB33F52}.Release|ORBIS.ActiveCfg = Release|ORBIS | |||
| {73F1A804-1116-46C3-922A-9C0ADEB33F52}.Release|ORBIS.Build.0 = Release|ORBIS | |||
| {73F1A804-1116-46C3-922A-9C0ADEB33F52}.Release|Win32.ActiveCfg = Release|Win32 | |||
| {73F1A804-1116-46C3-922A-9C0ADEB33F52}.Release|Win32.Build.0 = Release|Win32 | |||
| {73F1A804-1116-46C3-922A-9C0ADEB33F52}.Release|x64.ActiveCfg = Release|x64 | |||
| {73F1A804-1116-46C3-922A-9C0ADEB33F52}.Release|x64.Build.0 = Release|x64 | |||
| {81C83B42-D00A-4FA3-9A3D-80F9D46524BF}.Debug|ORBIS.ActiveCfg = Debug|ORBIS | |||
| {81C83B42-D00A-4FA3-9A3D-80F9D46524BF}.Debug|ORBIS.Build.0 = Debug|ORBIS | |||
| {81C83B42-D00A-4FA3-9A3D-80F9D46524BF}.Debug|Win32.ActiveCfg = Debug|Win32 | |||
| @@ -351,8 +335,6 @@ Global | |||
| {E05E23A5-67DE-42B5-98A3-E63CCE0CC0AF} = {E74CF679-CA2A-47E9-B1F4-3779D6AC6B04} | |||
| {E4DFEBF9-C310-462F-9876-7EB59C1E4D4E} = {1AFD580B-98B8-4689-B661-38C41132C60E} | |||
| {F59FA82C-DDB9-4EE2-80AE-CB0E4C6567A4} = {E74CF679-CA2A-47E9-B1F4-3779D6AC6B04} | |||
| {4C4BD478-3767-4C27-BD91-DAAFE7CD03A2} = {3D341D8A-E400-4B1D-BC05-B5C35487D9B5} | |||
| {73F1A804-1116-46C3-922A-9C0ADEB33F52} = {4C4BD478-3767-4C27-BD91-DAAFE7CD03A2} | |||
| {B6297FF2-63D0-41EE-BE13-EFF720C9B0FA} = {1AFD580B-98B8-4689-B661-38C41132C60E} | |||
| {81C83B42-D00A-4FA3-9A3D-80F9D46524BF} = {E74CF679-CA2A-47E9-B1F4-3779D6AC6B04} | |||
| {31B96262-1C41-43B9-BA38-27AA385B05DB} = {E74CF679-CA2A-47E9-B1F4-3779D6AC6B04} | |||
| @@ -21,8 +21,6 @@ AM_DEFAULT_VERBOSITY=0 | |||
| dnl Versioning of the separate software we ship | |||
| LOLUNIT_VERSION=0.1 | |||
| AC_SUBST(LOLUNIT_VERSION) | |||
| LOLREMEZ_VERSION=0.2 | |||
| AC_SUBST(LOLREMEZ_VERSION) | |||
| AC_SUBST(lol_srcdir, '${top_srcdir}') | |||
| AC_SUBST(lol_builddir, '${top_builddir}') | |||
| @@ -262,7 +260,6 @@ AC_CONFIG_FILES( | |||
| doc/samples/sandbox/Makefile | |||
| doc/tutorial/Makefile | |||
| tools/Makefile | |||
| tools/lolremez/Makefile | |||
| tools/lolunit/Makefile | |||
| tools/vimlol/Makefile | |||
| tools/vslol/Makefile | |||
| @@ -2,7 +2,6 @@ | |||
| include $(top_srcdir)/build/autotools/common.am | |||
| SUBDIRS = | |||
| SUBDIRS += lolremez | |||
| SUBDIRS += lolunit | |||
| SUBDIRS += vimlol | |||
| SUBDIRS += vslol | |||
| @@ -1,2 +0,0 @@ | |||
| # Our binaries | |||
| lolremez | |||
| @@ -1,14 +0,0 @@ | |||
| include $(top_srcdir)/build/autotools/common.am | |||
| EXTRA_DIST += NEWS.txt lolremez.sln lolremez.vcxproj lolremez.vcxproj.filters | |||
| if BUILD_TOOLS | |||
| noinst_PROGRAMS = lolremez | |||
| endif | |||
| lolremez_SOURCES = \ | |||
| lolremez.cpp solver.cpp solver.h matrix.h expression.h | |||
| lolremez_CPPFLAGS = $(AM_CPPFLAGS) | |||
| lolremez_DEPENDENCIES = @LOL_DEPS@ | |||
| @@ -1,13 +0,0 @@ | |||
| News for LolRemez 0.2: | |||
| - significant performance and accuracy improvements thanks to various | |||
| bugfixes and a better extrema finder for the error function. | |||
| - user can now define accuracy of the final result. | |||
| - exp(), sin(), cos() and tan() are now about 20% faster. | |||
| - multiplying a real number by an integer power of two is now a virtually | |||
| free operation. | |||
| - fixed a rounding bug in the real number printing routine. | |||
| Initial release: LolRemez 0.1 | |||
| @@ -1,349 +0,0 @@ | |||
| // | |||
| // LolRemez — Remez algorithm implementation | |||
| // | |||
| // Copyright © 2005—2015 Sam Hocevar <sam@hocevar.net> | |||
| // | |||
| // This program is free software. It comes without any warranty, to | |||
| // the extent permitted by applicable law. You can redistribute it | |||
| // and/or modify it under the terms of the Do What the Fuck You Want | |||
| // to Public License, Version 2, as published by the WTFPL Task Force. | |||
| // See http://www.wtfpl.net/ for more details. | |||
| // | |||
| #pragma once | |||
| // | |||
| // Powerful arithmetic expression parser/evaluator | |||
| // | |||
| // Usage: | |||
| // expression e; | |||
| // e.parse(" 2*x^3 + 3 * sin(x - atan(x))"); | |||
| // auto y = e.eval("1.5"); | |||
| // | |||
| #include "pegtl.hh" | |||
| namespace grammar | |||
| { | |||
| using namespace pegtl; | |||
| enum class id : uint8_t | |||
| { | |||
| /* Variables and constants */ | |||
| x, | |||
| constant, | |||
| /* Unary functions/operators */ | |||
| plus, minus, abs, | |||
| sqrt, cbrt, | |||
| exp, exp2, log, log2, log10, | |||
| sin, cos, tan, | |||
| asin, acos, atan, | |||
| sinh, cosh, tanh, | |||
| /* Binary functions/operators */ | |||
| add, sub, mul, div, | |||
| atan2, pow, | |||
| min, max, | |||
| }; | |||
| struct expression | |||
| { | |||
| /* | |||
| * Evaluate expression at x | |||
| */ | |||
| lol::real eval(lol::real const &x) const | |||
| { | |||
| /* Use a stack */ | |||
| lol::array<lol::real> stack; | |||
| for (int i = 0; i < m_ops.count(); ++i) | |||
| { | |||
| /* Rules that do not consume stack elements */ | |||
| if (m_ops[i].m1 == id::x) | |||
| { | |||
| stack.push(x); | |||
| continue; | |||
| } | |||
| else if (m_ops[i].m1 == id::constant) | |||
| { | |||
| stack.push(m_constants[m_ops[i].m2]); | |||
| continue; | |||
| } | |||
| /* All other rules consume at least the head of the stack */ | |||
| lol::real head = stack.pop(); | |||
| switch (m_ops[i].m1) | |||
| { | |||
| case id::plus: stack.push(head); break; | |||
| case id::minus: stack.push(-head); break; | |||
| case id::abs: stack.push(fabs(head)); break; | |||
| case id::sqrt: stack.push(sqrt(head)); break; | |||
| case id::cbrt: stack.push(cbrt(head)); break; | |||
| case id::exp: stack.push(exp(head)); break; | |||
| case id::exp2: stack.push(exp2(head)); break; | |||
| case id::log: stack.push(log(head)); break; | |||
| case id::log2: stack.push(log2(head)); break; | |||
| case id::log10: stack.push(log10(head)); break; | |||
| case id::sin: stack.push(sin(head)); break; | |||
| case id::cos: stack.push(cos(head)); break; | |||
| case id::tan: stack.push(tan(head)); break; | |||
| case id::asin: stack.push(asin(head)); break; | |||
| case id::acos: stack.push(acos(head)); break; | |||
| case id::atan: stack.push(atan(head)); break; | |||
| case id::sinh: stack.push(sinh(head)); break; | |||
| case id::cosh: stack.push(cosh(head)); break; | |||
| case id::tanh: stack.push(tanh(head)); break; | |||
| case id::add: stack.push(stack.pop() + head); break; | |||
| case id::sub: stack.push(stack.pop() - head); break; | |||
| case id::mul: stack.push(stack.pop() * head); break; | |||
| case id::div: stack.push(stack.pop() / head); break; | |||
| case id::atan2: stack.push(atan2(stack.pop(), head)); break; | |||
| case id::pow: stack.push(pow(stack.pop(), head)); break; | |||
| case id::min: stack.push(min(stack.pop(), head)); break; | |||
| case id::max: stack.push(max(stack.pop(), head)); break; | |||
| case id::x: | |||
| case id::constant: | |||
| /* Already handled above */ | |||
| break; | |||
| } | |||
| } | |||
| ASSERT(stack.count() == 1); | |||
| return stack.pop(); | |||
| } | |||
| private: | |||
| lol::array<id, int> m_ops; | |||
| lol::array<lol::real> m_constants; | |||
| private: | |||
| struct r_expr; | |||
| // r_ <- <blank> * | |||
| struct _ : star<space> {}; | |||
| // r_constant <- <digit> + ( "." <digit> * ) ? ( [eE] [+-] ? <digit> + ) ? | |||
| struct r_constant : seq<plus<digit>, | |||
| opt<seq<one<'.'>, | |||
| star<digit>>>, | |||
| opt<seq<one<'e', 'E'>, | |||
| opt<one<'+', '-'>>, | |||
| plus<digit>>>> {}; | |||
| // r_var <- "x" | |||
| struct r_var : pegtl_string_t("x") {}; | |||
| // r_binary_call <- <r_binary_fun> "(" r_expr "," r_expr ")" | |||
| struct r_binary_fun : sor<pegtl_string_t("atan2"), | |||
| pegtl_string_t("pow"), | |||
| pegtl_string_t("min"), | |||
| pegtl_string_t("max")> {}; | |||
| struct r_binary_call : seq<r_binary_fun, | |||
| _, one<'('>, | |||
| _, r_expr, | |||
| _, one<','>, | |||
| _, r_expr, | |||
| _, one<')'>> {}; | |||
| // r_unary_call <- <r_unary_fun> "(" r_expr ")" | |||
| struct r_unary_fun : sor<pegtl_string_t("abs"), | |||
| pegtl_string_t("sqrt"), | |||
| pegtl_string_t("cbrt"), | |||
| pegtl_string_t("exp"), | |||
| pegtl_string_t("exp2"), | |||
| pegtl_string_t("log"), | |||
| pegtl_string_t("log2"), | |||
| pegtl_string_t("log10"), | |||
| pegtl_string_t("sin"), | |||
| pegtl_string_t("cos"), | |||
| pegtl_string_t("tan"), | |||
| pegtl_string_t("asin"), | |||
| pegtl_string_t("acos"), | |||
| pegtl_string_t("atan"), | |||
| pegtl_string_t("sinh"), | |||
| pegtl_string_t("cosh"), | |||
| pegtl_string_t("tanh")> {}; | |||
| struct r_unary_call : seq<r_unary_fun, | |||
| _, one<'('>, | |||
| _, r_expr, | |||
| _, one<')'>> {}; | |||
| // r_call <- r_binary_call / r_unary_call | |||
| struct r_call : sor<r_binary_call, | |||
| r_unary_call> {}; | |||
| // r_parentheses <- "(" r_expr ")" | |||
| struct r_parentheses : seq<one<'('>, | |||
| pad<r_expr, space>, | |||
| one<')'>> {}; | |||
| // r_terminal <- r_call / r_var / r_constant / r_parentheses | |||
| struct r_terminal : sor<r_call, | |||
| r_var, | |||
| r_constant, | |||
| r_parentheses> {}; | |||
| // r_signed <- "-" r_signed / "+" r_signed / r_terminal | |||
| struct r_signed; | |||
| struct r_minus : seq<one<'-'>, _, r_signed> {}; | |||
| struct r_signed : sor<r_minus, | |||
| seq<one<'+'>, _, r_signed>, | |||
| r_terminal> {}; | |||
| // r_exponent <- ( "^" / "**" ) r_signed | |||
| struct r_exponent : seq<pad<sor<one<'^'>, | |||
| string<'*', '*'>>, space>, | |||
| r_signed> {}; | |||
| // r_factor <- r_signed ( r_exponent ) * | |||
| struct r_factor : seq<r_signed, | |||
| star<r_exponent>> {}; | |||
| // r_mul <- "*" r_factor | |||
| // r_div <- "/" r_factor | |||
| // r_term <- r_factor ( r_mul / r_div ) * | |||
| struct r_mul : seq<_, one<'*'>, _, r_factor> {}; | |||
| struct r_div : seq<_, one<'/'>, _, r_factor> {}; | |||
| struct r_term : seq<r_factor, | |||
| star<sor<r_mul, r_div>>> {}; | |||
| // r_add <- "+" r_term | |||
| // r_sub <- "-" r_term | |||
| // r_expr <- r_term ( r_add / r_sub ) * | |||
| struct r_add : seq<_, one<'+'>, _, r_term> {}; | |||
| struct r_sub : seq<_, one<'-'>, _, r_term> {}; | |||
| struct r_expr : seq<r_term, | |||
| star<sor<r_add, r_sub>>> {}; | |||
| // r_stmt <- r_expr <end> | |||
| struct r_stmt : must<pad<r_expr, space>, pegtl::eof> {}; | |||
| // | |||
| // Default actions | |||
| // | |||
| template<typename R> | |||
| struct action : nothing<R> {}; | |||
| template<id OP> | |||
| struct generic_action | |||
| { | |||
| static void apply(action_input const &in, expression *that) | |||
| { | |||
| UNUSED(in); | |||
| that->m_ops.push(OP, -1); | |||
| } | |||
| }; | |||
| public: | |||
| /* | |||
| * Parse arithmetic expression in x, e.g. 2*x+3 | |||
| */ | |||
| void parse(std::string const &str) | |||
| { | |||
| m_ops.empty(); | |||
| m_constants.empty(); | |||
| pegtl::parse_string<r_stmt, action>(str, "expression", this); | |||
| } | |||
| }; | |||
| // | |||
| // Rule specialisations for simple operators | |||
| // | |||
| template<> struct expression::action<expression::r_var> : generic_action<id::x> {}; | |||
| template<> struct expression::action<expression::r_exponent> : generic_action<id::pow> {}; | |||
| template<> struct expression::action<expression::r_mul> : generic_action<id::mul> {}; | |||
| template<> struct expression::action<expression::r_div> : generic_action<id::div> {}; | |||
| template<> struct expression::action<expression::r_add> : generic_action<id::add> {}; | |||
| template<> struct expression::action<expression::r_sub> : generic_action<id::sub> {}; | |||
| template<> struct expression::action<expression::r_minus> : generic_action<id::minus> {}; | |||
| // | |||
| // Rule specialisations for unary and binary function calls | |||
| // | |||
| template<> | |||
| struct expression::action<expression::r_binary_call> | |||
| { | |||
| static void apply(action_input const &in, expression *that) | |||
| { | |||
| struct { id ret; char const *name; } lut[] = | |||
| { | |||
| { id::atan2, "atan2" }, | |||
| { id::pow, "pow" }, | |||
| { id::min, "min" }, | |||
| { id::max, "max" }, | |||
| }; | |||
| for (auto pair : lut) | |||
| { | |||
| if (strncmp(in.string().c_str(), pair.name, strlen(pair.name)) != 0) | |||
| continue; | |||
| that->m_ops.push(pair.ret, -1); | |||
| return; | |||
| } | |||
| } | |||
| }; | |||
| template<> | |||
| struct expression::action<expression::r_unary_call> | |||
| { | |||
| static void apply(action_input const &in, expression *that) | |||
| { | |||
| struct { id ret; char const *name; } lut[] = | |||
| { | |||
| { id::abs, "abs" }, | |||
| { id::sqrt, "sqrt" }, | |||
| { id::cbrt, "cbrt" }, | |||
| { id::exp2, "exp2" }, | |||
| { id::exp, "exp" }, | |||
| { id::log10, "log10" }, | |||
| { id::log2, "log2" }, | |||
| { id::log, "log" }, | |||
| { id::sinh, "sinh" }, | |||
| { id::cosh, "cosh" }, | |||
| { id::tanh, "tanh" }, | |||
| { id::sin, "sin" }, | |||
| { id::cos, "cos" }, | |||
| { id::tan, "tan" }, | |||
| { id::asin, "asin" }, | |||
| { id::acos, "acos" }, | |||
| { id::atan, "atan" }, | |||
| }; | |||
| for (auto pair : lut) | |||
| { | |||
| if (strncmp(in.string().c_str(), pair.name, strlen(pair.name)) != 0) | |||
| continue; | |||
| that->m_ops.push(pair.ret, -1); | |||
| return; | |||
| } | |||
| } | |||
| }; | |||
| template<> | |||
| struct expression::action<expression::r_constant> | |||
| { | |||
| static void apply(action_input const &in, expression *that) | |||
| { | |||
| /* FIXME: check if the constant is already in the list */ | |||
| that->m_ops.push(id::constant, that->m_constants.count()); | |||
| that->m_constants.push(lol::real(in.string().c_str())); | |||
| } | |||
| }; | |||
| } /* namespace grammar */ | |||
| using grammar::expression; | |||
| @@ -1,129 +0,0 @@ | |||
| // | |||
| // LolRemez - Remez algorithm implementation | |||
| // | |||
| // Copyright © 2005—2016 Sam Hocevar <sam@hocevar.net> | |||
| // | |||
| // This program is free software. It comes without any warranty, to | |||
| // the extent permitted by applicable law. You can redistribute it | |||
| // and/or modify it under the terms of the Do What the Fuck You Want | |||
| // to Public License, Version 2, as published by the WTFPL Task Force. | |||
| // See http://www.wtfpl.net/ for more details. | |||
| // | |||
| #if HAVE_CONFIG_H | |||
| # include "config.h" | |||
| #endif | |||
| #include <lol/engine.h> | |||
| #include <lol/math/real.h> | |||
| #include "solver.h" | |||
| #include "expression.h" | |||
| using lol::array; | |||
| using lol::real; | |||
| using lol::String; | |||
| static void version(void) | |||
| { | |||
| printf("lolremez %s\n", PACKAGE_VERSION); | |||
| printf("Copyright © 2005—2016 Sam Hocevar <sam@hocevar.net>\n"); | |||
| printf("This program is free software. It comes without any warranty, to the extent\n"); | |||
| printf("permitted by applicable law. You can redistribute it and/or modify it under\n"); | |||
| printf("the terms of the Do What the Fuck You Want to Public License, Version 2, as\n"); | |||
| printf("published by the WTFPL Task Force. See http://www.wtfpl.net/ for more details.\n"); | |||
| printf("\n"); | |||
| printf("Written by Sam Hocevar. Report bugs to <sam@hocevar.net>.\n"); | |||
| } | |||
| static void usage() | |||
| { | |||
| printf("Usage: lolremez [-d degree] [-r xmin:xmax] x-expression [x-error]\n"); | |||
| printf(" lolremez -h | --help\n"); | |||
| printf(" lolremez -V | --version\n"); | |||
| printf("Find a polynomial approximation for x-expression.\n"); | |||
| printf("\n"); | |||
| printf("Mandatory arguments to long options are mandatory for short options too.\n"); | |||
| printf(" -d, --degree <degree> degree of final polynomial\n"); | |||
| printf(" -r, --range <xmin>:<xmax> range over which to approximate\n"); | |||
| printf(" -h, --help display this help and exit\n"); | |||
| printf(" -V, --version output version information and exit\n"); | |||
| printf("\n"); | |||
| printf("Examples:\n"); | |||
| printf(" lolremez -d 4 -r -1:1 \"atan(exp(1+x))\"\n"); | |||
| printf(" lolremez -d 4 -r -1:1 \"atan(exp(1+x))\" \"exp(1+x)\"\n"); | |||
| printf("\n"); | |||
| printf("Written by Sam Hocevar. Report bugs to <sam@hocevar.net>.\n"); | |||
| } | |||
| static void FAIL(char const *message = nullptr) | |||
| { | |||
| if (message) | |||
| printf("Error: %s\n", message); | |||
| printf("Try 'lolremez --help' for more information.\n"); | |||
| exit(EXIT_FAILURE); | |||
| } | |||
| /* See the tutorial at http://lolengine.net/wiki/doc/maths/remez */ | |||
| int main(int argc, char **argv) | |||
| { | |||
| String xmin("-1"), xmax("1"); | |||
| char const *f = nullptr, *g = nullptr; | |||
| int degree = 4; | |||
| lol::getopt opt(argc, argv); | |||
| opt.add_opt('h', "help", false); | |||
| opt.add_opt('v', "version", false); | |||
| opt.add_opt('d', "degree", true); | |||
| opt.add_opt('r', "range", true); | |||
| for (;;) | |||
| { | |||
| int c = opt.parse(); | |||
| if (c == -1) | |||
| break; | |||
| switch (c) | |||
| { | |||
| case 'd': /* --degree */ | |||
| degree = atoi(opt.arg); | |||
| break; | |||
| case 'r': { /* --range */ | |||
| array<String> arg = String(opt.arg).split(':'); | |||
| if (arg.count() != 2) | |||
| FAIL("invalid range"); | |||
| xmin = arg[0]; | |||
| xmax = arg[1]; | |||
| } break; | |||
| case 'h': /* --help */ | |||
| usage(); | |||
| return EXIT_SUCCESS; | |||
| case 'v': /* --version */ | |||
| version(); | |||
| return EXIT_SUCCESS; | |||
| default: | |||
| FAIL(); | |||
| } | |||
| } | |||
| if (opt.index < argc) | |||
| f = argv[opt.index++]; | |||
| if (opt.index < argc) | |||
| g = argv[opt.index++]; | |||
| if (!f) | |||
| FAIL("no function specified"); | |||
| else if (opt.index < argc) | |||
| FAIL("too many arguments"); | |||
| if (real(xmin.C()) >= real(xmax.C())) | |||
| FAIL("invalid range"); | |||
| remez_solver solver(degree, 20); | |||
| solver.run(xmin.C(), xmax.C(), f, g); | |||
| return 0; | |||
| } | |||
| @@ -1,31 +0,0 @@ | |||
| | |||
| Microsoft Visual Studio Solution File, Format Version 11.00 | |||
| # Visual Studio 2010 | |||
| Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "lolremez", "lolremez.vcxproj", "{A2767731-6FBF-442C-900F-301D095516E7}" | |||
| EndProject | |||
| Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{663F5D32-D212-4A88-A681-84B54D3188A3}" | |||
| ProjectSection(SolutionItems) = preProject | |||
| Performance1.psess = Performance1.psess | |||
| EndProjectSection | |||
| EndProject | |||
| Global | |||
| GlobalSection(SolutionConfigurationPlatforms) = preSolution | |||
| Debug|Win32 = Debug|Win32 | |||
| Debug|x64 = Debug|x64 | |||
| Release|Win32 = Release|Win32 | |||
| Release|x64 = Release|x64 | |||
| EndGlobalSection | |||
| GlobalSection(ProjectConfigurationPlatforms) = postSolution | |||
| {A2767731-6FBF-442C-900F-301D095516E7}.Debug|Win32.ActiveCfg = Debug|Win32 | |||
| {A2767731-6FBF-442C-900F-301D095516E7}.Debug|Win32.Build.0 = Debug|Win32 | |||
| {A2767731-6FBF-442C-900F-301D095516E7}.Debug|x64.ActiveCfg = Debug|x64 | |||
| {A2767731-6FBF-442C-900F-301D095516E7}.Debug|x64.Build.0 = Debug|x64 | |||
| {A2767731-6FBF-442C-900F-301D095516E7}.Release|Win32.ActiveCfg = Release|Win32 | |||
| {A2767731-6FBF-442C-900F-301D095516E7}.Release|Win32.Build.0 = Release|Win32 | |||
| {A2767731-6FBF-442C-900F-301D095516E7}.Release|x64.ActiveCfg = Release|x64 | |||
| {A2767731-6FBF-442C-900F-301D095516E7}.Release|x64.Build.0 = Release|x64 | |||
| EndGlobalSection | |||
| GlobalSection(SolutionProperties) = preSolution | |||
| HideSolutionNode = FALSE | |||
| EndGlobalSection | |||
| EndGlobal | |||
| @@ -1,73 +0,0 @@ | |||
| <?xml version="1.0" encoding="utf-8"?> | |||
| <Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> | |||
| <PropertyGroup Label="LolMacros"> | |||
| <LolDir Condition="Exists('$(SolutionDir)\lol')">$(SolutionDir)\lol</LolDir> | |||
| <LolDir Condition="!Exists('$(SolutionDir)\lol')">$(SolutionDir)\..</LolDir> | |||
| </PropertyGroup> | |||
| <ItemGroup Label="ProjectConfigurations"> | |||
| <ProjectConfiguration Include="Debug|ORBIS"> | |||
| <Configuration>Debug</Configuration> | |||
| <Platform>ORBIS</Platform> | |||
| </ProjectConfiguration> | |||
| <ProjectConfiguration Include="Debug|Win32"> | |||
| <Configuration>Debug</Configuration> | |||
| <Platform>Win32</Platform> | |||
| </ProjectConfiguration> | |||
| <ProjectConfiguration Include="Debug|x64"> | |||
| <Configuration>Debug</Configuration> | |||
| <Platform>x64</Platform> | |||
| </ProjectConfiguration> | |||
| <ProjectConfiguration Include="Release|ORBIS"> | |||
| <Configuration>Release</Configuration> | |||
| <Platform>ORBIS</Platform> | |||
| </ProjectConfiguration> | |||
| <ProjectConfiguration Include="Release|Win32"> | |||
| <Configuration>Release</Configuration> | |||
| <Platform>Win32</Platform> | |||
| </ProjectConfiguration> | |||
| <ProjectConfiguration Include="Release|x64"> | |||
| <Configuration>Release</Configuration> | |||
| <Platform>x64</Platform> | |||
| </ProjectConfiguration> | |||
| </ItemGroup> | |||
| <ItemGroup> | |||
| <ClInclude Include="expression.h" /> | |||
| <ClInclude Include="matrix.h" /> | |||
| <ClInclude Include="solver.h" /> | |||
| </ItemGroup> | |||
| <ItemGroup> | |||
| <ClCompile Include="lolremez.cpp" /> | |||
| <ClCompile Include="solver.cpp" /> | |||
| </ItemGroup> | |||
| <ItemGroup> | |||
| <ProjectReference Include="$(LolDir)\src\lol-core.vcxproj"> | |||
| <Project>{9e62f2fe-3408-4eae-8238-fd84238ceeda}</Project> | |||
| </ProjectReference> | |||
| <ProjectReference Include="$(LolDir)\src\3rdparty\lol-bullet.vcxproj"> | |||
| <Project>{83d3b207-c601-4025-8f41-01dedc354661}</Project> | |||
| </ProjectReference> | |||
| <ProjectReference Include="$(LolDir)\src\3rdparty\lol-lua.vcxproj"> | |||
| <Project>{d84021ca-b233-4e0f-8a52-071b83bbccc4}</Project> | |||
| </ProjectReference> | |||
| </ItemGroup> | |||
| <PropertyGroup Label="Globals"> | |||
| <ProjectGuid>{73F1A804-1116-46C3-922A-9C0ADEB33F52}</ProjectGuid> | |||
| <ConfigurationType>Application</ConfigurationType> | |||
| <Keyword>Win32Proj</Keyword> | |||
| </PropertyGroup> | |||
| <Import Project="$(LolDir)\build\msbuild\lol.config.props" /> | |||
| <ImportGroup Label="ExtensionSettings"> | |||
| <Import Project="$(LolDir)\build\msbuild\lolfx.props" /> | |||
| </ImportGroup> | |||
| <ImportGroup Label="PropertySheets"> | |||
| <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> | |||
| <Import Project="$(LolDir)\build\msbuild\lol.vars.props" /> | |||
| </ImportGroup> | |||
| <PropertyGroup Label="UserMacros" /> | |||
| <Import Project="$(LolDir)\build\msbuild\lol.rules.props" /> | |||
| <ItemDefinitionGroup /> | |||
| <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> | |||
| <ImportGroup Label="ExtensionTargets"> | |||
| <Import Project="$(LolDir)\build\msbuild\lolfx.targets" /> | |||
| </ImportGroup> | |||
| </Project> | |||
| @@ -1,12 +0,0 @@ | |||
| <?xml version="1.0" encoding="utf-8"?> | |||
| <Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> | |||
| <ItemGroup> | |||
| <ClCompile Include="lolremez.cpp" /> | |||
| <ClCompile Include="solver.cpp" /> | |||
| </ItemGroup> | |||
| <ItemGroup> | |||
| <ClInclude Include="expression.h" /> | |||
| <ClInclude Include="matrix.h" /> | |||
| <ClInclude Include="solver.h" /> | |||
| </ItemGroup> | |||
| </Project> | |||
| @@ -1,97 +0,0 @@ | |||
| // | |||
| // LolRemez - Remez algorithm implementation | |||
| // | |||
| // Copyright © 2005—2015 Sam Hocevar <sam@hocevar.net> | |||
| // | |||
| // This program is free software. It comes without any warranty, to | |||
| // the extent permitted by applicable law. You can redistribute it | |||
| // and/or modify it under the terms of the Do What the Fuck You Want | |||
| // to Public License, Version 2, as published by the WTFPL Task Force. | |||
| // See http://www.wtfpl.net/ for more details. | |||
| // | |||
| #pragma once | |||
| using namespace lol; | |||
| /* | |||
| * Arbitrarily-sized square matrices; for now this only supports | |||
| * naive inversion and is used for the Remez inversion method. | |||
| */ | |||
| template<typename T> | |||
| struct linear_system : public array2d<T> | |||
| { | |||
| inline linear_system<T>(int cols) | |||
| { | |||
| ASSERT(cols > 0); | |||
| this->resize(ivec2(cols)); | |||
| } | |||
| void init(T const &x) | |||
| { | |||
| int const n = this->size().x; | |||
| for (int j = 0; j < n; j++) | |||
| for (int i = 0; i < n; i++) | |||
| (*this)[i][j] = (i == j) ? x : (T)0; | |||
| } | |||
| /* Naive matrix inversion */ | |||
| linear_system<T> inverse() const | |||
| { | |||
| int const n = this->size().x; | |||
| linear_system a(*this), b(n); | |||
| b.init((T)1); | |||
| /* Inversion method: iterate through all columns and make sure | |||
| * all the terms are 1 on the diagonal and 0 everywhere else */ | |||
| for (int i = 0; i < n; i++) | |||
| { | |||
| /* If the expected coefficient is zero, add one of | |||
| * the other lines. The first we meet will do. */ | |||
| if (!a[i][i]) | |||
| { | |||
| for (int j = i + 1; j < n; j++) | |||
| { | |||
| if (!a[i][j]) | |||
| continue; | |||
| /* Add row j to row i */ | |||
| for (int k = 0; k < n; k++) | |||
| { | |||
| a[k][i] += a[k][j]; | |||
| b[k][i] += b[k][j]; | |||
| } | |||
| break; | |||
| } | |||
| } | |||
| /* Now we know the diagonal term is non-zero. Get its inverse | |||
| * and use that to nullify all other terms in the column */ | |||
| T x = (T)1 / a[i][i]; | |||
| for (int j = 0; j < n; j++) | |||
| { | |||
| if (j == i) | |||
| continue; | |||
| T mul = x * a[i][j]; | |||
| for (int k = 0; k < n; k++) | |||
| { | |||
| a[k][j] -= mul * a[k][i]; | |||
| b[k][j] -= mul * b[k][i]; | |||
| } | |||
| } | |||
| /* Finally, ensure the diagonal term is 1 */ | |||
| for (int k = 0; k < n; k++) | |||
| { | |||
| a[k][i] *= x; | |||
| b[k][i] *= x; | |||
| } | |||
| } | |||
| return b; | |||
| } | |||
| }; | |||
| @@ -1,419 +0,0 @@ | |||
| // | |||
| // LolRemez - Remez algorithm implementation | |||
| // | |||
| // Copyright © 2005—2015 Sam Hocevar <sam@hocevar.net> | |||
| // | |||
| // This program is free software. It comes without any warranty, to | |||
| // the extent permitted by applicable law. You can redistribute it | |||
| // and/or modify it under the terms of the Do What the Fuck You Want | |||
| // to Public License, Version 2, as published by the WTFPL Task Force. | |||
| // See http://www.wtfpl.net/ for more details. | |||
| // | |||
| #if HAVE_CONFIG_H | |||
| # include "config.h" | |||
| #endif | |||
| #include <functional> | |||
| #include <lol/engine.h> | |||
| #include <lol/math/real.h> | |||
| #include <lol/math/polynomial.h> | |||
| #include "matrix.h" | |||
| #include "solver.h" | |||
| using lol::real; | |||
| remez_solver::remez_solver(int order, int decimals) | |||
| : m_order(order), | |||
| m_decimals(decimals), | |||
| m_has_weight(false) | |||
| { | |||
| /* Spawn 4 worker threads */ | |||
| for (int i = 0; i < 4; ++i) | |||
| { | |||
| auto th = new thread(std::bind(&remez_solver::worker_thread, this)); | |||
| m_workers.push(th); | |||
| } | |||
| } | |||
| remez_solver::~remez_solver() | |||
| { | |||
| /* Signal worker threads to quit, wait for worker threads to answer, | |||
| * and kill worker threads. */ | |||
| for (auto worker : m_workers) | |||
| UNUSED(worker), m_questions.push(-1); | |||
| for (auto worker : m_workers) | |||
| UNUSED(worker), m_answers.pop(); | |||
| for (auto worker : m_workers) | |||
| delete worker; | |||
| } | |||
| void remez_solver::run(real a, real b, char const *func, char const *weight) | |||
| { | |||
| m_func.parse(func); | |||
| if (weight) | |||
| { | |||
| m_weight.parse(weight); | |||
| m_has_weight = true; | |||
| } | |||
| m_k1 = (b + a) / 2; | |||
| m_k2 = (b - a) / 2; | |||
| m_epsilon = pow((real)10, (real)-(m_decimals + 2)); | |||
| remez_init(); | |||
| print_poly(); | |||
| for (int n = 0; ; n++) | |||
| { | |||
| real old_error = m_error; | |||
| find_extrema(); | |||
| remez_step(); | |||
| if (m_error >= (real)0 | |||
| && fabs(m_error - old_error) < m_error * m_epsilon) | |||
| break; | |||
| print_poly(); | |||
| find_zeroes(); | |||
| } | |||
| print_poly(); | |||
| } | |||
| /* | |||
| * This is basically the first Remez step: we solve a system of | |||
| * order N+1 and get a good initial polynomial estimate. | |||
| */ | |||
| void remez_solver::remez_init() | |||
| { | |||
| /* m_order + 1 zeroes of the error function */ | |||
| m_zeroes.resize(m_order + 1); | |||
| /* m_order + 1 zeroes to find */ | |||
| m_zeroes_state.resize(m_order + 1); | |||
| /* m_order + 2 control points */ | |||
| m_control.resize(m_order + 2); | |||
| /* m_order extrema to find */ | |||
| m_extrema_state.resize(m_order); | |||
| /* Initial estimates for the x_i where the error will be zero and | |||
| * precompute f(x_i). */ | |||
| array<real> fxn; | |||
| for (int i = 0; i < m_order + 1; i++) | |||
| { | |||
| m_zeroes[i] = (real)(2 * i - m_order) / (real)(m_order + 1); | |||
| fxn.push(eval_func(m_zeroes[i])); | |||
| } | |||
| /* We build a matrix of Chebyshev evaluations: row i contains the | |||
| * evaluations of x_i for polynomial order n = 0, 1, ... */ | |||
| linear_system<real> system(m_order + 1); | |||
| for (int n = 0; n < m_order + 1; n++) | |||
| { | |||
| auto p = polynomial<real>::chebyshev(n); | |||
| for (int i = 0; i < m_order + 1; i++) | |||
| system[i][n] = p.eval(m_zeroes[i]); | |||
| } | |||
| /* Solve the system */ | |||
| system = system.inverse(); | |||
| /* Compute new Chebyshev estimate */ | |||
| m_estimate = polynomial<real>(); | |||
| for (int n = 0; n < m_order + 1; n++) | |||
| { | |||
| real weight = 0; | |||
| for (int i = 0; i < m_order + 1; i++) | |||
| weight += system[n][i] * fxn[i]; | |||
| m_estimate += weight * polynomial<real>::chebyshev(n); | |||
| } | |||
| } | |||
| /* | |||
| * Every subsequent iteration of the Remez algorithm: we solve a system | |||
| * of order N+2 to both refine the estimate and compute the error. | |||
| */ | |||
| void remez_solver::remez_step() | |||
| { | |||
| Timer t; | |||
| /* Pick up x_i where error will be 0 and compute f(x_i) */ | |||
| array<real> fxn; | |||
| for (int i = 0; i < m_order + 2; i++) | |||
| fxn.push(eval_func(m_control[i])); | |||
| /* We build a matrix of Chebyshev evaluations: row i contains the | |||
| * evaluations of x_i for polynomial order n = 0, 1, ... */ | |||
| linear_system<real> system(m_order + 2); | |||
| for (int n = 0; n < m_order + 1; n++) | |||
| { | |||
| auto p = polynomial<real>::chebyshev(n); | |||
| for (int i = 0; i < m_order + 2; i++) | |||
| system[i][n] = p.eval(m_control[i]); | |||
| } | |||
| /* The last line of the system is the oscillating error */ | |||
| for (int i = 0; i < m_order + 2; i++) | |||
| { | |||
| real error = fabs(eval_weight(m_control[i])); | |||
| system[i][m_order + 1] = (i & 1) ? error : -error; | |||
| } | |||
| /* Solve the system */ | |||
| system = system.inverse(); | |||
| /* Compute new polynomial estimate */ | |||
| m_estimate = polynomial<real>(); | |||
| for (int n = 0; n < m_order + 1; n++) | |||
| { | |||
| real weight = 0; | |||
| for (int i = 0; i < m_order + 2; i++) | |||
| weight += system[n][i] * fxn[i]; | |||
| m_estimate += weight * polynomial<real>::chebyshev(n); | |||
| } | |||
| /* Compute the error (FIXME: unused?) */ | |||
| real error = 0; | |||
| for (int i = 0; i < m_order + 2; i++) | |||
| error += system[m_order + 1][i] * fxn[i]; | |||
| using std::printf; | |||
| printf(" -:- timing for inversion: %f ms\n", t.Get() * 1000.f); | |||
| } | |||
| /* | |||
| * Find m_order + 1 zeroes of the error function. No need to compute the | |||
| * relative error: its zeroes are at the same place as the absolute error! | |||
| * | |||
| * The algorithm used here is naïve regula falsi. It still performs a lot | |||
| * better than the midpoint algorithm. | |||
| */ | |||
| void remez_solver::find_zeroes() | |||
| { | |||
| Timer t; | |||
| static real const limit = ldexp((real)1, -500); | |||
| static real const zero = (real)0; | |||
| /* Initialise an [a,b] bracket for each zero we try to find */ | |||
| for (int i = 0; i < m_order + 1; i++) | |||
| { | |||
| point &a = m_zeroes_state[i].m1; | |||
| point &b = m_zeroes_state[i].m2; | |||
| a.x = m_control[i]; | |||
| a.err = eval_estimate(a.x) - eval_func(a.x); | |||
| b.x = m_control[i + 1]; | |||
| b.err = eval_estimate(b.x) - eval_func(b.x); | |||
| m_questions.push(i); | |||
| } | |||
| /* Watch all brackets for updates from worker threads */ | |||
| for (int finished = 0; finished < m_order + 1; ) | |||
| { | |||
| int i = m_answers.pop(); | |||
| point &a = m_zeroes_state[i].m1; | |||
| point &b = m_zeroes_state[i].m2; | |||
| point &c = m_zeroes_state[i].m3; | |||
| if (c.err == zero || fabs(a.x - b.x) <= limit) | |||
| { | |||
| m_zeroes[i] = c.x; | |||
| ++finished; | |||
| continue; | |||
| } | |||
| m_questions.push(i); | |||
| } | |||
| using std::printf; | |||
| printf(" -:- timing for zeroes: %f ms\n", t.Get() * 1000.f); | |||
| } | |||
| /* | |||
| * Find m_order extrema of the error function. We maximise the relative | |||
| * error, since its extrema are at slightly different locations than the | |||
| * absolute error’s. | |||
| * | |||
| * The algorithm used here is successive parabolic interpolation. FIXME: we | |||
| * could use Brent’s method instead, which combines parabolic interpolation | |||
| * and golden ratio search and has superlinear convergence. | |||
| */ | |||
| void remez_solver::find_extrema() | |||
| { | |||
| Timer t; | |||
| m_control[0] = -1; | |||
| m_control[m_order + 1] = 1; | |||
| m_error = 0; | |||
| /* Initialise an [a,b,c] bracket for each extremum we try to find */ | |||
| for (int i = 0; i < m_order; i++) | |||
| { | |||
| point &a = m_extrema_state[i].m1; | |||
| point &b = m_extrema_state[i].m2; | |||
| point &c = m_extrema_state[i].m3; | |||
| a.x = m_zeroes[i]; | |||
| b.x = m_zeroes[i + 1]; | |||
| c.x = a.x + (b.x - a.x) * real(rand(0.4f, 0.6f)); | |||
| a.err = eval_error(a.x); | |||
| b.err = eval_error(b.x); | |||
| c.err = eval_error(c.x); | |||
| m_questions.push(i + 1000); | |||
| } | |||
| /* Watch all brackets for updates from worker threads */ | |||
| for (int finished = 0; finished < m_order; ) | |||
| { | |||
| int i = m_answers.pop(); | |||
| point &a = m_extrema_state[i - 1000].m1; | |||
| point &b = m_extrema_state[i - 1000].m2; | |||
| point &c = m_extrema_state[i - 1000].m3; | |||
| if (b.x - a.x <= m_epsilon) | |||
| { | |||
| m_control[i - 1000 + 1] = c.x; | |||
| if (c.err > m_error) | |||
| m_error = c.err; | |||
| ++finished; | |||
| continue; | |||
| } | |||
| m_questions.push(i); | |||
| } | |||
| using std::printf; | |||
| printf(" -:- timing for extrema: %f ms\n", t.Get() * 1000.f); | |||
| printf(" -:- error: "); | |||
| m_error.print(m_decimals); | |||
| printf("\n"); | |||
| } | |||
| void remez_solver::print_poly() | |||
| { | |||
| /* Transform our polynomial in the [-1..1] range into a polynomial | |||
| * in the [a..b] range by composing it with q: | |||
| * q(x) = 2x / (b-a) - (b+a) / (b-a) */ | |||
| polynomial<real> q ({ -m_k1 / m_k2, real(1) / m_k2 }); | |||
| polynomial<real> r = m_estimate.eval(q); | |||
| using std::printf; | |||
| printf("\n"); | |||
| for (int j = 0; j < m_order + 1; j++) | |||
| { | |||
| if (j) | |||
| printf(" + x**%i * ", j); | |||
| r[j].print(m_decimals); | |||
| } | |||
| printf("\n\n"); | |||
| } | |||
| real remez_solver::eval_estimate(real const &x) | |||
| { | |||
| return m_estimate.eval(x); | |||
| } | |||
| real remez_solver::eval_func(real const &x) | |||
| { | |||
| return m_func.eval(x * m_k2 + m_k1); | |||
| } | |||
| real remez_solver::eval_weight(real const &x) | |||
| { | |||
| return m_has_weight ? m_weight.eval(x * m_k2 + m_k1) : real(1); | |||
| } | |||
| real remez_solver::eval_error(real const &x) | |||
| { | |||
| return fabs((eval_estimate(x) - eval_func(x)) / eval_weight(x)); | |||
| } | |||
| void remez_solver::worker_thread() | |||
| { | |||
| static real const zero = (real)0; | |||
| for (;;) | |||
| { | |||
| int i = m_questions.pop(); | |||
| if (i < 0) | |||
| { | |||
| m_answers.push(i); | |||
| break; | |||
| } | |||
| else if (i < 1000) | |||
| { | |||
| point &a = m_zeroes_state[i].m1; | |||
| point &b = m_zeroes_state[i].m2; | |||
| point &c = m_zeroes_state[i].m3; | |||
| real s = abs(b.err) / (abs(a.err) + abs(b.err)); | |||
| real newc = b.x + s * (a.x - b.x); | |||
| /* If the third point didn't change since last iteration, | |||
| * we may be at an inflection point. Use the midpoint to get | |||
| * out of this situation. */ | |||
| c.x = newc != c.x ? newc : (a.x + b.x) / 2; | |||
| c.err = eval_estimate(c.x) - eval_func(c.x); | |||
| if ((a.err < zero && c.err < zero) | |||
| || (a.err > zero && c.err > zero)) | |||
| a = c; | |||
| else | |||
| b = c; | |||
| m_answers.push(i); | |||
| } | |||
| else if (i < 2000) | |||
| { | |||
| point &a = m_extrema_state[i - 1000].m1; | |||
| point &b = m_extrema_state[i - 1000].m2; | |||
| point &c = m_extrema_state[i - 1000].m3; | |||
| point d; | |||
| real d1 = c.x - a.x, d2 = c.x - b.x; | |||
| real k1 = d1 * (c.err - b.err); | |||
| real k2 = d2 * (c.err - a.err); | |||
| d.x = c.x - (d1 * k1 - d2 * k2) / (k1 - k2) / 2; | |||
| /* If parabolic interpolation failed, pick a number | |||
| * inbetween. */ | |||
| if (d.x <= a.x || d.x >= b.x) | |||
| d.x = (a.x + b.x) / 2; | |||
| d.err = eval_error(d.x); | |||
| /* Update bracketing depending on the new point. */ | |||
| if (d.err < c.err) | |||
| { | |||
| (d.x > c.x ? b : a) = d; | |||
| } | |||
| else | |||
| { | |||
| (d.x > c.x ? a : b) = c; | |||
| c = d; | |||
| } | |||
| m_answers.push(i); | |||
| } | |||
| } | |||
| } | |||
| @@ -1,75 +0,0 @@ | |||
| // | |||
| // LolRemez - Remez algorithm implementation | |||
| // | |||
| // Copyright © 2005—2015 Sam Hocevar <sam@hocevar.net> | |||
| // | |||
| // This program is free software. It comes without any warranty, to | |||
| // the extent permitted by applicable law. You can redistribute it | |||
| // and/or modify it under the terms of the Do What the Fuck You Want | |||
| // to Public License, Version 2, as published by the WTFPL Task Force. | |||
| // See http://www.wtfpl.net/ for more details. | |||
| // | |||
| #pragma once | |||
| // | |||
| // The remez_solver class | |||
| // ---------------------- | |||
| // | |||
| #include <cstdio> | |||
| #include "expression.h" | |||
| class remez_solver | |||
| { | |||
| public: | |||
| remez_solver(int order, int decimals); | |||
| ~remez_solver(); | |||
| void run(lol::real a, lol::real b, | |||
| char const *func, char const *weight = nullptr); | |||
| private: | |||
| void remez_init(); | |||
| void remez_step(); | |||
| void find_zeroes(); | |||
| void find_extrema(); | |||
| void worker_thread(); | |||
| void print_poly(); | |||
| lol::real eval_estimate(lol::real const &x); | |||
| lol::real eval_func(lol::real const &x); | |||
| lol::real eval_weight(lol::real const &x); | |||
| lol::real eval_error(lol::real const &x); | |||
| private: | |||
| /* User-defined parameters */ | |||
| expression m_func, m_weight; | |||
| int m_order, m_decimals; | |||
| bool m_has_weight; | |||
| /* Solver state */ | |||
| lol::polynomial<lol::real> m_estimate; | |||
| lol::array<lol::real> m_zeroes; | |||
| lol::array<lol::real> m_control; | |||
| lol::real m_k1, m_k2, m_epsilon, m_error; | |||
| struct point | |||
| { | |||
| lol::real x, err; | |||
| }; | |||
| lol::array<point, point, point> m_zeroes_state; | |||
| lol::array<point, point, point> m_extrema_state; | |||
| /* Threading information */ | |||
| lol::array<lol::thread *> m_workers; | |||
| lol::queue<int> m_questions, m_answers; | |||
| }; | |||