Repository: dharmatech/Symbolism Branch: master Commit: e10887dcf9e2 Files: 31 Total size: 336.5 KB Directory structure: gitextract_i2j1v7op/ ├── .gitattributes ├── .gitignore ├── Examples/ │ ├── pse-example-5.10-Obj.md │ ├── symbolism-linux.md │ └── unit-test-index.md ├── README.md ├── Symbolism/ │ ├── AlgebraicExpand.cs │ ├── CoefficientGpe.cs │ ├── DeepSelect.cs │ ├── DegreeGpe.cs │ ├── EliminateVariable.cs │ ├── ExpandPower.cs │ ├── ExpandProduct.cs │ ├── Has.cs │ ├── IsolateVariable.cs │ ├── LeadingCoefficientGpe.cs │ ├── LogicalExpand.cs │ ├── PolynomialDivision.cs │ ├── PolynomialGcd.cs │ ├── RationalExpand.cs │ ├── RationalizeExpression.cs │ ├── SimplifyEquation.cs │ ├── SimplifyLogical.cs │ ├── Substitute.cs │ ├── Symbolism.cs │ ├── Symbolism.csproj │ ├── Trigonometric.cs │ └── Utils.cs ├── Symbolism.sln └── Tests/ ├── Tests.cs └── Tests.csproj ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitattributes ================================================ # Auto detect text files and perform LF normalization * text=auto # Custom for Visual Studio *.cs diff=csharp *.sln merge=union *.csproj merge=union *.vbproj merge=union *.fsproj merge=union *.dbproj merge=union # Standard to msysgit *.doc diff=astextplain *.DOC diff=astextplain *.docx diff=astextplain *.DOCX diff=astextplain *.dot diff=astextplain *.DOT diff=astextplain *.pdf diff=astextplain *.PDF diff=astextplain *.rtf diff=astextplain *.RTF diff=astextplain ================================================ FILE: .gitignore ================================================ ## Ignore Visual Studio temporary files, build results, and ## files generated by popular Visual Studio add-ons. ## ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore # User-specific files *.rsuser *.suo *.user *.userosscache *.sln.docstates # User-specific files (MonoDevelop/Xamarin Studio) *.userprefs # Build results [Dd]ebug/ [Dd]ebugPublic/ [Rr]elease/ [Rr]eleases/ x64/ x86/ bld/ [Bb]in/ [Oo]bj/ [Ll]og/ # Visual Studio 2015/2017 cache/options directory .vs/ # Uncomment if you have tasks that create the project's static files in wwwroot #wwwroot/ # Visual Studio 2017 auto generated files Generated\ Files/ # MSTest test Results [Tt]est[Rr]esult*/ [Bb]uild[Ll]og.* # NUNIT *.VisualState.xml TestResult.xml # Build Results of an ATL Project [Dd]ebugPS/ [Rr]eleasePS/ dlldata.c # Benchmark Results BenchmarkDotNet.Artifacts/ # .NET Core project.lock.json project.fragment.lock.json artifacts/ # StyleCop StyleCopReport.xml # Files built by Visual Studio *_i.c *_p.c *_h.h *.ilk *.meta *.obj *.iobj *.pch *.pdb *.ipdb *.pgc *.pgd *.rsp *.sbr *.tlb *.tli *.tlh *.tmp *.tmp_proj *_wpftmp.csproj *.log *.vspscc *.vssscc .builds *.pidb *.svclog *.scc # Chutzpah Test files _Chutzpah* # Visual C++ cache files ipch/ *.aps *.ncb *.opendb *.opensdf *.sdf *.cachefile *.VC.db *.VC.VC.opendb # Visual Studio profiler *.psess *.vsp *.vspx *.sap # Visual Studio Trace Files *.e2e # TFS 2012 Local Workspace $tf/ # Guidance Automation Toolkit *.gpState # ReSharper is a .NET coding add-in _ReSharper*/ *.[Rr]e[Ss]harper *.DotSettings.user # JustCode is a .NET coding add-in .JustCode # TeamCity is a build add-in _TeamCity* # DotCover is a Code Coverage Tool *.dotCover # AxoCover is a Code Coverage Tool .axoCover/* !.axoCover/settings.json # Visual Studio code coverage results *.coverage *.coveragexml # NCrunch _NCrunch_* .*crunch*.local.xml nCrunchTemp_* # MightyMoose *.mm.* AutoTest.Net/ # Web workbench (sass) .sass-cache/ # Installshield output folder [Ee]xpress/ # DocProject is a documentation generator add-in DocProject/buildhelp/ DocProject/Help/*.HxT DocProject/Help/*.HxC DocProject/Help/*.hhc DocProject/Help/*.hhk DocProject/Help/*.hhp DocProject/Help/Html2 DocProject/Help/html # Click-Once directory publish/ # Publish Web Output *.[Pp]ublish.xml *.azurePubxml # Note: Comment the next line if you want to checkin your web deploy settings, # but database connection strings (with potential passwords) will be unencrypted *.pubxml *.publishproj # Microsoft Azure Web App publish settings. Comment the next line if you want to # checkin your Azure Web App publish settings, but sensitive information contained # in these scripts will be unencrypted PublishScripts/ # NuGet Packages *.nupkg # The packages folder can be ignored because of Package Restore **/[Pp]ackages/* # except build/, which is used as an MSBuild target. !**/[Pp]ackages/build/ # Uncomment if necessary however generally it will be regenerated when needed #!**/[Pp]ackages/repositories.config # NuGet v3's project.json files produces more ignorable files *.nuget.props *.nuget.targets # Microsoft Azure Build Output csx/ *.build.csdef # Microsoft Azure Emulator ecf/ rcf/ # Windows Store app package directories and files AppPackages/ BundleArtifacts/ Package.StoreAssociation.xml _pkginfo.txt *.appx # Visual Studio cache files # files ending in .cache can be ignored *.[Cc]ache # but keep track of directories ending in .cache !*.[Cc]ache/ # Others ClientBin/ ~$* *~ *.dbmdl *.dbproj.schemaview *.jfm *.pfx *.publishsettings orleans.codegen.cs # Including strong name files can present a security risk # (https://github.com/github/gitignore/pull/2483#issue-259490424) #*.snk # Since there are multiple workflows, uncomment next line to ignore bower_components # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) #bower_components/ # RIA/Silverlight projects Generated_Code/ # Backup & report files from converting an old project file # to a newer Visual Studio version. Backup files are not needed, # because we have git ;-) _UpgradeReport_Files/ Backup*/ UpgradeLog*.XML UpgradeLog*.htm ServiceFabricBackup/ *.rptproj.bak # SQL Server files *.mdf *.ldf *.ndf # Business Intelligence projects *.rdl.data *.bim.layout *.bim_*.settings *.rptproj.rsuser # Microsoft Fakes FakesAssemblies/ # GhostDoc plugin setting file *.GhostDoc.xml # Node.js Tools for Visual Studio .ntvs_analysis.dat node_modules/ # Visual Studio 6 build log *.plg # Visual Studio 6 workspace options file *.opt # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) *.vbw # Visual Studio LightSwitch build output **/*.HTMLClient/GeneratedArtifacts **/*.DesktopClient/GeneratedArtifacts **/*.DesktopClient/ModelManifest.xml **/*.Server/GeneratedArtifacts **/*.Server/ModelManifest.xml _Pvt_Extensions # Paket dependency manager .paket/paket.exe paket-files/ # FAKE - F# Make .fake/ # JetBrains Rider .idea/ *.sln.iml # CodeRush personal settings .cr/personal # Python Tools for Visual Studio (PTVS) __pycache__/ *.pyc # Cake - Uncomment if you are using it # tools/** # !tools/packages.config # Tabs Studio *.tss # Telerik's JustMock configuration file *.jmconfig # BizTalk build output *.btp.cs *.btm.cs *.odx.cs *.xsd.cs # OpenCover UI analysis results OpenCover/ # Azure Stream Analytics local run output ASALocalRun/ # MSBuild Binary and Structured Log *.binlog # NVidia Nsight GPU debugger configuration file *.nvuser # MFractors (Xamarin productivity tool) working folder .mfractor/ # Local History for Visual Studio .localhistory/ ================================================ FILE: Examples/pse-example-5.10-Obj.md ================================================ # Classes for representing forces on objects In [this walkthrough](https://gist.github.com/dharmatech/a14d1a29a7d4c0728d37), one thing you may notice is the large number of `Symbol` objects that are defined. For example, for just the ball, the following are defined: ```C# var F1_m1 = new Symbol("F1_m1"); // force 1 on mass 1 var F2_m1 = new Symbol("F2_m1"); // force 2 on mass 1 var th1_m1 = new Symbol("th1_m1"); // direction of force 1 on mass 1 var th2_m1 = new Symbol("th2_m1"); // direction of force 2 on mass 1 var F1x_m1 = new Symbol("F1x_m1"); // x-component of force 1 on mass 1 var F2x_m1 = new Symbol("F2x_m1"); // x-component of force 2 on mass 1 var F1y_m1 = new Symbol("F1y_m1"); // y-component of force 1 on mass 1 var F2y_m1 = new Symbol("F2y_m1"); // y-component of force 2 on mass 1 var Fx_m1 = new Symbol("Fx_m1"); // x-component of total force on mass 1 var Fy_m1 = new Symbol("Fy_m1"); // y-component of total force on mass 1 var ax_m1 = new Symbol("ax_m1"); // x-component of acceleration of mass 1 var ay_m1 = new Symbol("ay_m1"); // y-component of acceleration of mass 1 ``` Then we have the many equations for the ball: ```C# F1x_m1 == F1_m1 * cos(th1_m1), F2x_m1 == F2_m1 * cos(th2_m1), F1y_m1 == F1_m1 * sin(th1_m1), F2y_m1 == F2_m1 * sin(th2_m1), Fx_m1 == F1x_m1 + F2x_m1, Fy_m1 == F1y_m1 + F2y_m1, Fx_m1 == m1 * ax_m1, Fy_m1 == m1 * ay_m1, ``` That's just for an object with two forces acting on it! The block, which has three forces acting on it, has more even more `Symbol`s and more equations. I've defined a class [`Obj2`](https://github.com/dharmatech/Symbolism/blob/ffa322d81cc0e8de932b3ae2d3dcf7b90b7c5bfd/Tests/Tests.cs#L112) which represents an object with two forces acting on it. So now, instead of all those symbols and equations for the ball, we write: ```C# var bal = new Obj2("bal"); ``` A nice benefit is that now the ball's associated symbols are object members and can be accessed easily via intellisense: ![](http://i.imgur.com/cNVIAsW.png) Moreover, the laws of motion equations can be generated with the `Equations` method: ```C# bal.Equations().DispLong() ``` displays the following at the console: ![](http://i.imgur.com/lCgEJec.png) The block, which has three forces acting on it is similarly represented with an `Obj3`: ```C# var blk = new Obj3("blk"); ``` Now the definition of the equations, which [took up about 40 lines](https://github.com/dharmatech/Symbolism/blob/ffa322d81cc0e8de932b3ae2d3dcf7b90b7c5bfd/Tests/Tests.cs#L3595-L3635) in the original example, is simply: ```C# var eqs = new And( blk.ax == bal.ay, // the block moves right as the ball moves up a == blk.ax, bal.Equations(), blk.Equations() ); ``` Let's display the resulting set of equations: ```C# eqs.DispLong() ``` ![](http://i.imgur.com/1OaYQhW.png) The symbolic values are also specified in terms of the members of `bal` and `blk`: ```C# var vals = new List { bal.ax == 0, bal.m == m1, bal.F1 == T, bal.th1 == (90).ToRadians(), // force 1 is straight up bal.F2 == m1 * g, bal.th2 == (270).ToRadians(), // force 2 is straight down blk.ay == 0, blk.m == m2, blk.F1 == n, blk.th1 == (90).ToRadians(), // force 1 is straight up blk.F2 == T, blk.th2 == (180).ToRadians(), // force 2 is straight down blk.F3 == m2 * g, blk.th3 == (270).ToRadians() + th // force 3 direction }; ``` If we substitute those values: ```C# eqs.SubstituteEqLs(vals).DispLong() ``` the equations become: ![](http://i.imgur.com/tE1EuQp.png) OK, let's find the acceleration. We eliminate unknown values and isolate `a`: ```C# eqs .SubstituteEqLs(vals) .EliminateVariables( bal.ΣFx, bal.F1x, bal.F2x, bal.ΣFy, bal.F1y, bal.F2y, blk.ΣFx, blk.F1x, blk.F2x, blk.F3x, blk.ΣFy, blk.F1y, blk.F2y, blk.F3y, blk.ax, bal.ay, T, n ) .IsolateVariable(a) .DispLong() ``` to get: ![](http://i.imgur.com/9NjZZ7b.png) This version of the example in terms of `Obj2` and `Obj3` is checked in as a [test](https://github.com/dharmatech/Symbolism/blob/ffa322d81cc0e8de932b3ae2d3dcf7b90b7c5bfd/Tests/Tests.cs#L3730). ================================================ FILE: Examples/symbolism-linux.md ================================================ This is a tutorial on how to build and run a simple Symbolism program on Linux. The following instructions have been tested on Ubuntu 16.04.3. Install [.NET Core SDK](https://dotnet.microsoft.com/download). Install [Visual Studio Code](https://code.visualstudio.com/). Install the [C# for Visual Studio Code](https://marketplace.visualstudio.com/items?itemName=ms-vscode.csharp) extension. Create a new console project: $ mkdir symbolism-example $ cd symbolism-example $ dotnet new console Add the Symbolism nuget package: $ dotnet add package Symbolism Start Visual Studio Code. $ code Select "File -> Open Folder" from the vscode menu. Select the *symbolism-example* folder. Select `Program.cs` in the Explorer pane. Replace the code with the following which solves a simple physics exercise: ``` csharp using System; using System.Collections.Generic; using System.Linq; using Symbolism; using Symbolism.Substitute; using Symbolism.EliminateVariable; using Symbolism.IsolateVariable; using Symbolism.LogicalExpand; using Symbolism.SimplifyLogical; using Symbolism.Utils; using static Symbolism.Constructors; using static Symbolism.Trigonometric.Constructors; namespace symbolism_example { class Program { static void Main(string[] args) { // In a local bar, a customer slides an empty beer mug // down the counter for a refill. The bartender is momentarily // distracted and does not see the mug, which slides // off the counter and strikes the floor 1.40 m from the // base of the counter. If the height of the counter is // 0.860 m, (a) with what velocity did the mug leave the // counter and (b) what was the direction of the mug’s // velocity just before it hit the floor? var xA = new Symbol("xA"); var yA = new Symbol("yA"); var xB = new Symbol("xB"); var yB = new Symbol("yB"); var vxA = new Symbol("vxA"); var vyA = new Symbol("vyA"); var vxB = new Symbol("vxB"); var vyB = new Symbol("vyB"); var tAB = new Symbol("tAB"); var ax = new Symbol("ax"); var ay = new Symbol("ay"); var thB = new Symbol("thB"); var vB = new Symbol("vB"); var eqs = and( vxB == vxA + ax * tAB, vyB == vyA + ay * tAB, tan(thB) == vyB / vxB, xB == xA + vxA * tAB + ax * (tAB ^ 2) / 2, yB == yA + vyA * tAB + ay * (tAB ^ 2) / 2, xB != 0 ); var vals = new List() { xA == 0, yA == 0.86, /* vxA */ vyA == 0, xB == 1.4, yB == 0, /* vxB vyB vB thB */ /* tAB */ ax == 0, ay == -9.8 }; var zeros = vals.Where(eq => eq.b == 0).ToList(); DoubleFloat.tolerance = 0.00001; eqs .SubstituteEqLs(zeros) .EliminateVariables(thB, vxB, vyB, tAB) .IsolateVariable(vxA) .LogicalExpand() .DispLong() .SubstituteEqLs(vals) .Disp(); eqs .SubstituteEqLs(zeros) .EliminateVariables(vxB, vyB, tAB, vxA) .LogicalExpand() .CheckVariable(xB) .SimplifyLogical() .IsolateVariable(thB) .DispLong() .SubstituteEqLs(vals) .Disp(); DoubleFloat.tolerance = null; } } } ``` Press `ctrl+f5` to run the program. The symbolic and numeric answers will be displayed in the console: ![](https://i.imgur.com/GIlICgo.png) ================================================ FILE: Examples/unit-test-index.md ================================================ Index of examples in unit tests. # Physics Examples ## Motion in Two Dimensions * [PSE 5E E4.3](https://github.com/dharmatech/Symbolism/blob/e6cf94395a92127b7b25c97748ced065433f00b1/Tests/Tests.cs#L1450) * [PSE 5E P4.9](https://github.com/dharmatech/Symbolism/blob/e6cf94395a92127b7b25c97748ced065433f00b1/Tests/Tests.cs#L5686) * [PSE 5E P4.11](https://github.com/dharmatech/Symbolism/blob/e6cf94395a92127b7b25c97748ced065433f00b1/Tests/Tests.cs#L5686) * [PSE 5E P4.13](https://github.com/dharmatech/Symbolism/blob/531deeccc941bfa891302fd1e02090780c52d49b/Tests/Tests.cs#L2419) * [PSE 5E P4.15](https://github.com/dharmatech/Symbolism/blob/531deeccc941bfa891302fd1e02090780c52d49b/Tests/Tests.cs#L2499) * [PSE 5E P4.17](https://github.com/dharmatech/Symbolism/blob/531deeccc941bfa891302fd1e02090780c52d49b/Tests/Tests.cs#L2590) * [PSE 5E P4.19](https://github.com/dharmatech/Symbolism/blob/531deeccc941bfa891302fd1e02090780c52d49b/Tests/Tests.cs#L2702) * [PSE 5E P4.21](https://github.com/dharmatech/Symbolism/blob/531deeccc941bfa891302fd1e02090780c52d49b/Tests/Tests.cs#L2808) * [PSE 5E P4.23](https://github.com/dharmatech/Symbolism/blob/531deeccc941bfa891302fd1e02090780c52d49b/Tests/Tests.cs#L2890) ## The Laws of Motion * [PSE 5E E5.1](https://github.com/dharmatech/Symbolism/blob/e6cf94395a92127b7b25c97748ced065433f00b1/Tests/Tests.cs#L3124) * [PSE 5E E5.4](https://github.com/dharmatech/Symbolism/blob/450ece65ce4ca4f196b512cba0f5ddeabd024690/Tests/Tests.cs#L3308) * [PSE 5E E5.6](https://github.com/dharmatech/Symbolism/blob/450ece65ce4ca4f196b512cba0f5ddeabd024690/Tests/Tests.cs#L3424) ## Work and Kinetic Energy * [PSE 5E E7.8](https://github.com/dharmatech/Symbolism/blob/e6cf94395a92127b7b25c97748ced065433f00b1/Tests/Tests.cs#L5519) * [PSE 5E E7.11](https://github.com/dharmatech/Symbolism/blob/e6cf94395a92127b7b25c97748ced065433f00b1/Tests/Tests.cs#L5596) * [PSE 6E P7.3](https://github.com/dharmatech/Symbolism/blob/e6cf94395a92127b7b25c97748ced065433f00b1/Tests/Tests.cs#L5686) * [PSE 5E P7.23](https://github.com/dharmatech/Symbolism/blob/8f8f618f1f2f2f38c6c4961222b19b8feb4739bf/Tests/Tests.cs#L5738) * [PSE 5E P7.33](https://github.com/dharmatech/Symbolism/blob/531deeccc941bfa891302fd1e02090780c52d49b/Tests/Tests.cs#L5782) * [PSE 5E P7.35](https://github.com/dharmatech/Symbolism/blob/531deeccc941bfa891302fd1e02090780c52d49b/Tests/Tests.cs#L5893) * [PSE 5E P7.39](https://github.com/dharmatech/Symbolism/blob/531deeccc941bfa891302fd1e02090780c52d49b/Tests/Tests.cs#L6026) * [PSE 5E P7.41](https://github.com/dharmatech/Symbolism/blob/531deeccc941bfa891302fd1e02090780c52d49b/Tests/Tests.cs#L6094) * [PSE 5E P7.55](https://github.com/dharmatech/Symbolism/blob/531deeccc941bfa891302fd1e02090780c52d49b/Tests/Tests.cs#L6183) ## Potential Energy and Conservation of Energy * [PSE 5E E8.2](https://github.com/dharmatech/Symbolism/blob/39d80bcee6ed96f14bc5cf6b787f62a326e9f410/Tests/Tests.cs#L6220) * [PSE 5E E8.3](https://github.com/dharmatech/Symbolism/blob/39d80bcee6ed96f14bc5cf6b787f62a326e9f410/Tests/Tests.cs#L6306) ================================================ FILE: README.md ================================================ # Symbolism - A Computer Algebra Library for C# Library that implements automatic simplification of algebraic expressions in C#. To get an idea of the kinds of expressions it handles, see the [tests in this file](Tests/Tests.cs). The automatic simplification algorithm comes from the book "Computer Algebra and Symbolic Computation: Mathematical Methods" by Joel S. Cohen. The core of the system is in [Symbolism.cs](Symbolism/Symbolism.cs). Symbolism currently targets .NET Standard 2.0 and thus can be used in .NET projects on Windows, Linux, and Mac. Nuget package [available here](https://www.nuget.org/packages/Symbolism/). # Setup on Linux [Tutorial](Examples/symbolism-linux.md) on how to build and run a simple Symbolism program on Linux. # Examples The "PSE 5E" examples and problems in the unit tests are from the textbook "Physics for Scientists and Engineers, 5th Edition" by Serway and Jewett. Here's an [index](Examples/unit-test-index.md) of some of the examples. [Here's a walk-through of solving a physics problem](https://gist.github.com/dharmatech/d6d499f14c808b159689). A slightly more complex [walk-through](https://gist.github.com/dharmatech/a5e74ef03d98b3ff1c45). [Walk-through](https://gist.github.com/dharmatech/a14d1a29a7d4c0728d37) of solving a laws of motion problem. In action: ![](http://i.imgur.com/7FH36o1.png) [Unit test](https://github.com/dharmatech/Symbolism/blob/ff09e2c20e026091225f4f303bbb06487a08f58d/Tests/Tests.cs#L2732) for that example. # See also Symbolism began as a port of the Scheme [MPL library](https://github.com/dharmatech/mpl) to C#. [GiNaC](http://www.ginac.de/) and [SymbolicC++](http://issc.uj.ac.za/symbolic/symbolic.html) are of course very inspirational. # References Computer Algebra and Symbolic Computation: Elementary Algorithms by Joel S. Cohen Computer Algebra and Symbolic Computation: Mathematical Methods by Joel S. Cohen ================================================ FILE: Symbolism/AlgebraicExpand.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using Symbolism.ExpandProduct; using Symbolism.ExpandPower; namespace Symbolism { namespace AlgebraicExpand { public static class Extensions { public static MathObject AlgebraicExpand(this MathObject u) { if (u is Equation) { var eq = u as Equation; return eq.a.AlgebraicExpand() == eq.b.AlgebraicExpand(); } if (u is Sum) return (u as Sum).Map(elt => elt.AlgebraicExpand()); if (u is Product) { var v = (u as Product).elts[0]; return v.AlgebraicExpand() .ExpandProduct( (u / v).AlgebraicExpand() ); } if (u is Power) { var bas = (u as Power).bas; var exp = (u as Power).exp; if (exp is Integer && (exp as Integer).val >= 2) return bas.AlgebraicExpand().ExpandPower((exp as Integer).val); else return u; } if (u is Function) { var u_ = u as Function; return new Function( u_.name, u_.proc, u_.args.ConvertAll(elt => elt.AlgebraicExpand())) .Simplify(); } return u; } } } } ================================================ FILE: Symbolism/CoefficientGpe.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Numerics; using System.Text; using Symbolism.Has; namespace Symbolism { namespace CoefficientGpe { public static class Extensions { public static Tuple CoefficientMonomialGpe(this MathObject u, MathObject x) { if (u == x) return Tuple.Create((MathObject)1, (BigInteger)1); if (u is Power && (u as Power).bas == x && (u as Power).exp is Integer && ((u as Power).exp as Integer).val > 1) return Tuple.Create((MathObject)1, ((u as Power).exp as Integer).val); if (u is Product) { var m = (BigInteger) 0; var c = u; foreach (var elt in (u as Product).elts) { var f = elt.CoefficientMonomialGpe(x); if (f == null) return null; if (f.Item2 != 0) { m = f.Item2; c = u / (x ^ m); } } return Tuple.Create(c, m); } if (u.FreeOf(x)) return Tuple.Create(u, (BigInteger)0); return null; } public static MathObject CoefficientGpe(this MathObject u, MathObject x, BigInteger j) { if (!(u is Sum)) { var f = u.CoefficientMonomialGpe(x); if (f == null) return null; if (f.Item2 == j) return f.Item1; return 0; } if (u == x) return j == 1 ? 1 : 0; var c = (MathObject)0; foreach (var elt in (u as Sum).elts) { var f = elt.CoefficientMonomialGpe(x); if (f == null) return null; if (f.Item2 == j) c = c + f.Item1; } return c; } } } } ================================================ FILE: Symbolism/DeepSelect.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace Symbolism { namespace DeepSelect { public static class Extensions { public static MathObject DeepSelect(this MathObject obj, Func proc) { var result = proc(obj); if (result is Power) return (result as Power).bas.DeepSelect(proc) ^ (result as Power).exp.DeepSelect(proc); if (result is Or) return (result as Or).Map(elt => elt.DeepSelect(proc)); if (result is And) return (result as And).Map(elt => elt.DeepSelect(proc)); if (result is Equation) return new Equation( (result as Equation).a.DeepSelect(proc), (result as Equation).b.DeepSelect(proc), (result as Equation).Operator); if (result is Sum) return (result as Sum).Map(elt => elt.DeepSelect(proc)); if (result is Product) return (result as Product).Map(elt => elt.DeepSelect(proc)); return result; } } } } ================================================ FILE: Symbolism/DegreeGpe.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Numerics; using System.Text; using Symbolism.Has; namespace Symbolism { namespace DegreeGpe { public static class Extensions { public static BigInteger DegreeMonomialGpe(this MathObject u, List v) { if (v.All(u.FreeOf)) return 0; if (v.Contains(u)) return 1; if (u is Power && ((Power)u).exp is Integer && ((Integer)((Power)u).exp).val > 1) return ((Integer)((Power)u).exp).val; //if (u is Product) // return ((Product)u).elts.Select(elt => elt.DegreeMonomialGpe(v)).Sum(); if (u is Product) return ((Product)u).elts.Select(elt => elt.DegreeMonomialGpe(v)).Aggregate(BigInteger.Add); return 0; } public static BigInteger DegreeGpe(this MathObject u, List v) { if (u is Sum) return ((Sum)u).elts.Select(elt => elt.DegreeMonomialGpe(v)).Max(); return u.DegreeMonomialGpe(v); } } } } ================================================ FILE: Symbolism/EliminateVariable.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using Symbolism.Substitute; using Symbolism.Has; using Symbolism.AlgebraicExpand; using Symbolism.IsolateVariable; using Symbolism.SimplifyEquation; namespace Symbolism.EliminateVariable { public static class Extensions { public static MathObject CheckVariableEqLs(this List eqs, Symbol sym) { // (a == 10, a == 0) -> 10 == 0 -> false if (eqs.EliminateVariableEqLs(sym) == false) return false; // (1/a != 0 && a != 0) -> a != 0 if (eqs.Any(eq => eq.Operator == Equation.Operators.NotEqual && eq.a == sym && eq.b == 0) && eqs.Any(eq => eq.Operator == Equation.Operators.NotEqual && eq.a == 1 / sym && eq.b == 0)) return eqs .Where(eq => (eq.Operator == Equation.Operators.NotEqual && eq.a == 1 / sym && eq.b == 0) == false) .ToList() .CheckVariableEqLs(sym); // x + y == z && x / y == 0 && x != 0 -> false if (eqs.Any(eq => eq.Operator == Equation.Operators.Equal && eq.a.Numerator() == sym && eq.a.Denominator().FreeOf(sym) && eq.b == 0) && eqs.Any(eq => eq == (sym != 0))) return false; return And.FromRange(eqs.Select(eq => eq as MathObject)); } public static MathObject CheckVariable(this MathObject expr, Symbol sym) { // 1 / x == 0 // 1 / x^2 == 0 if (expr is Equation && (expr as Equation).Operator == Equation.Operators.Equal && (expr as Equation).b == 0 && (expr as Equation).a.Has(sym) && (expr as Equation).SimplifyEquation() is Equation && ((expr as Equation).SimplifyEquation() as Equation).a is Power && (((expr as Equation).SimplifyEquation() as Equation).a as Power).exp is Integer && ((((expr as Equation).SimplifyEquation() as Equation).a as Power).exp as Integer).val < 0) return false; if (expr is And) { var result = (expr as And).Map(elt => elt.CheckVariable(sym)); if (result is And) { var eqs = (expr as And).args.Select(elt => elt as Equation).ToList(); return eqs.CheckVariableEqLs(sym); } return result; } if (expr is Or && (expr as Or).args.All(elt => elt is And)) return (expr as Or).Map(elt => elt.CheckVariable(sym)); return expr; } // EliminateVarAnd // EliminateVarOr // EliminateVarLs // EliminateVar // EliminateVars public static MathObject EliminateVariableEqLs(this List eqs, Symbol sym) { if (eqs.Any(elt => elt.Operator == Equation.Operators.Equal && elt.Has(sym) && elt.AlgebraicExpand().Has(sym) && elt.IsolateVariableEq(sym).Has(obj => obj is Equation && (obj as Equation).a == sym && (obj as Equation).b.FreeOf(sym)) ) == false) return And.FromRange(eqs.Select(elt => elt as MathObject)); var eq = eqs.First(elt => elt.Operator == Equation.Operators.Equal && elt.Has(sym) && elt.AlgebraicExpand().Has(sym) && elt.IsolateVariableEq(sym).Has(obj => obj is Equation && (obj as Equation).a == sym && (obj as Equation).b.FreeOf(sym))); var rest = eqs.Except(new List() { eq }); var result = eq.IsolateVariableEq(sym); // sym was not isolated if (result is Equation && ((result as Equation).a != sym || (result as Equation).b.Has(sym))) return And.FromRange(eqs.Select(elt => elt as MathObject)); if (result is Equation) { var eq_sym = result as Equation; return And.FromRange(rest.Select(elt => elt.Substitute(sym, eq_sym.b))).Simplify(); // return new And() { args = rest.Select(rest_eq => rest_eq.SubstituteEq(eq_sym)).ToList() }; // rest.Map(rest_eq => rest_eq.Substitute(eq_sym) } // Or( // And(eq0, eq1, eq2, ...) // And(eq3, eq4, eq5, ...) // ) if (result is Or && (result as Or).args.All(elt => elt is And)) return (result as Or).Map(elt => (elt as And).AddRange(rest).EliminateVariable(sym)); if (result is Or) { var items = new List(); foreach (Equation eq_sym in (result as Or).args) items.Add(new And(rest.Select(rest_eq => rest_eq.Substitute(sym, eq_sym.b)).ToArray()).Simplify()); return Or.FromRange(items); } throw new Exception(); } public static MathObject EliminateVariable(this MathObject expr, Symbol sym) { if (expr is And) { var eqs = (expr as And).args.Select(elt => elt as Equation); return EliminateVariableEqLs(eqs.ToList(), sym); } if (expr is Or) { return Or.FromRange((expr as Or).args.Select(and_expr => and_expr.EliminateVariable(sym))); // expr.Map(and_expr => and_expr.EliminateVar(sym)) } throw new Exception(); } public static MathObject EliminateVariables(this MathObject expr, params Symbol[] syms) => syms.Aggregate(expr, (result, sym) => result.EliminateVariable(sym)); } } ================================================ FILE: Symbolism/ExpandPower.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Numerics; using Symbolism.ExpandProduct; namespace Symbolism { namespace ExpandPower { public static class Extensions { static BigInteger Factorial(BigInteger n) { var result = (BigInteger) 1; for (var i = 1; i <= n; i++) { result *= i; } return result; // return Enumerable.Range(1, n).Aggregate((acc, elt) => acc * elt); } public static MathObject ExpandPower(this MathObject u, BigInteger n) { if (u is Sum) { var f = (u as Sum).elts[0]; var r = u - f; MathObject s = 0; var k = 0; while (true) { if (k > n) return s; else { var c = Factorial(n) / (Factorial(k) * Factorial(n - k)); s = s + (c * (f ^ (n - k))).ExpandProduct(r.ExpandPower(k)); k++; } } } else return u ^ n; } } } } ================================================ FILE: Symbolism/ExpandProduct.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace Symbolism { namespace ExpandProduct { public static class Extensions { public static MathObject ExpandProduct(this MathObject r, MathObject s) { if (r is Sum) { var f = (r as Sum).elts[0]; return f.ExpandProduct(s) + (r - f).ExpandProduct(s); } if (s is Sum) return s.ExpandProduct(r); return r * s; } } } } ================================================ FILE: Symbolism/Has.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace Symbolism { namespace Has { public static class Extensions { public static bool Has(this MathObject obj, MathObject a) { if (obj == a) return true; if (obj is Equation) return (obj as Equation).a.Has(a) || (obj as Equation).b.Has(a); if (obj is Power) return (((Power)obj).bas.Has(a) || ((Power)obj).exp.Has(a)); if (obj is Product) return ((Product)obj).elts.Any(elt => elt.Has(a)); if (obj is Sum) return ((Sum)obj).elts.Any(elt => elt.Has(a)); if (obj is Function) return ((Function)obj).args.Any(elt => elt.Has(a)); return false; } public static bool Has(this MathObject obj, Func proc) { if (proc(obj)) return true; if (obj is Equation) return (obj as Equation).a.Has(proc) || (obj as Equation).b.Has(proc); if (obj is Power) return (obj as Power).bas.Has(proc) || (obj as Power).exp.Has(proc); if (obj is Product) return (obj as Product).elts.Any(elt => elt.Has(proc)); if (obj is Sum) return (obj as Sum).elts.Any(elt => elt.Has(proc)); if (obj is Function) return (obj as Function).args.Any(elt => elt.Has(proc)); return false; } public static bool FreeOf(this MathObject obj, MathObject a) => !obj.Has(a); } } } ================================================ FILE: Symbolism/IsolateVariable.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using Symbolism.Has; using Symbolism.Trigonometric; using Symbolism.CoefficientGpe; using Symbolism.AlgebraicExpand; using Symbolism.DegreeGpe; namespace Symbolism.IsolateVariable { public static class Extensions { public static MathObject IsolateVariableEq(this Equation eq, Symbol sym) { if (eq.Operator == Equation.Operators.NotEqual) return eq; if (eq.FreeOf(sym)) return eq; // sin(x) / cos(x) == y -> tan(x) == y if (eq.a is Product && (eq.a as Product).elts.Any(elt => elt == new Sin(sym)) && eq.a is Product && (eq.a as Product).elts.Any(elt => elt == 1 / new Cos(sym))) return (eq.a / new Sin(sym) * new Cos(sym) * new Tan(sym) == eq.b).IsolateVariableEq(sym); // A sin(x)^2 == B sin(x) cos(x) -> A sin(x)^2 / (B sin(x) cos(x)) == 1 if ( eq.a is Product && (eq.a as Product).elts.Any(elt => (elt == new Sin(sym)) || ((elt is Power) && (elt as Power).bas == new Sin(sym) && (elt as Power).exp is Number)) && eq.b is Product && (eq.b as Product).elts.Any(elt => (elt == new Sin(sym)) || ((elt is Power) && (elt as Power).bas == new Sin(sym) && (elt as Power).exp is Number)) ) return (eq.a / eq.b == 1).IsolateVariableEq(sym); if (eq.b.Has(sym)) return IsolateVariableEq(new Equation(eq.a - eq.b, 0), sym); if (eq.a == sym) return eq; // (a x^2 + c) / x == - b if (eq.a is Product && (eq.a as Product).elts.Any( elt => elt is Power && (elt as Power).bas == sym && (elt as Power).exp == -1)) return IsolateVariableEq(eq.a * sym == eq.b * sym, sym); //if (eq.a is Product && // (eq.a as Product).elts.Any( // elt => // elt is Power && // (elt as Power).bas == sym && // (elt as Power).exp is Integer && // ((elt as Power).exp as Integer).val < 0)) // return IsolateVariableEq(eq.a * sym == eq.b * sym, sym); // if (eq.a.Denominator() is Product && // (eq.a.Denominator() as Product).Any(elt => elt.Base() == sym) // // // (x + y)^(1/2) == z // // x == -y + z^2 && z >= 0 if (eq.a is Power && (eq.a as Power).exp == new Integer(1) / 2) return IsolateVariableEq((eq.a ^ 2) == (eq.b ^ 2), sym); // 1 / sqrt(x) == y if (eq.a is Power && (eq.a as Power).exp == -new Integer(1) / 2) return (eq.a / eq.a == eq.b / eq.a).IsolateVariable(sym); // x ^ 2 == y // x ^ 2 - y == 0 if (eq.a.AlgebraicExpand().DegreeGpe(new List() { sym }) == 2 && eq.b != 0) { return (eq.a - eq.b == 0).IsolateVariable(sym); } // a x^2 + b x + c == 0 if (eq.a.AlgebraicExpand().DegreeGpe(new List() { sym }) == 2) { var a = eq.a.AlgebraicExpand().CoefficientGpe(sym, 2); var b = eq.a.AlgebraicExpand().CoefficientGpe(sym, 1); var c = eq.a.AlgebraicExpand().CoefficientGpe(sym, 0); if (a == null || b == null || c == null) return eq; return new Or( new And( sym == (-b + (((b ^ 2) - 4 * a * c) ^ (new Integer(1) / 2))) / (2 * a), (a != 0).Simplify() ).Simplify(), new And( sym == (-b - (((b ^ 2) - 4 * a * c) ^ (new Integer(1) / 2))) / (2 * a), (a != 0).Simplify() ).Simplify(), new And(sym == -c / b, a == 0, (b != 0).Simplify()).Simplify(), new And( (a == 0).Simplify(), (b == 0).Simplify(), (c == 0).Simplify() ).Simplify() ).Simplify(); } // (x + y == z).IsolateVariable(x) if (eq.a is Sum && (eq.a as Sum).elts.Any(elt => elt.FreeOf(sym))) { var items = ((Sum)eq.a).elts.FindAll(elt => elt.FreeOf(sym)); //return IsolateVariable( // new Equation( // eq.a - new Sum() { elts = items }.Simplify(), // eq.b - new Sum() { elts = items }.Simplify()), // sym); //var new_a = eq.a; items.ForEach(elt => new_a = new_a - elt); //var new_b = eq.b; items.ForEach(elt => new_b = new_b - elt); var new_a = Sum.FromRange((eq.a as Sum).elts.Where(elt => items.Contains(elt) == false)).Simplify(); var new_b = eq.b; items.ForEach(elt => new_b = new_b - elt); // (new_a as Sum).Where(elt => items.Contains(elt) == false) return IsolateVariableEq(new Equation(new_a, new_b), sym); //return IsolateVariable( // new Equation( // eq.a + new Sum() { elts = items.ConvertAll(elt => elt * -1) }.Simplify(), // eq.b - new Sum() { elts = items }.Simplify()), // sym); } // a b + a c == d // a + a c == d if (eq.a is Sum && (eq.a as Sum).elts.All(elt => elt.DegreeGpe(new List() { sym }) == 1)) { //return // (new Sum() { elts = (eq.a as Sum).elts.Select(elt => elt / sym).ToList() }.Simplify() == eq.b / sym) // .IsolateVariable(sym); return (sym * Sum.FromRange((eq.a as Sum).elts.Select(elt => elt / sym)).Simplify() == eq.b) .IsolateVariable(sym); } // -sqrt(x) + z * x == y if (eq.a is Sum && eq.a.Has(sym ^ (new Integer(1) / 2))) return eq; // sqrt(a + x) - z * x == -y if (eq.a is Sum && eq.a.Has(elt => elt is Power && (elt as Power).exp == new Integer(1) / 2 && (elt as Power).bas.Has(sym))) return eq; if (eq.a is Sum && eq.AlgebraicExpand().Equals(eq)) return eq; if (eq.a is Sum) return eq.AlgebraicExpand().IsolateVariable(sym); // (x + 1) / (x + 2) == 3 if (eq.a.Numerator().Has(sym) && eq.a.Denominator().Has(sym)) { return IsolateVariableEq(eq.a * eq.a.Denominator() == eq.b * eq.a.Denominator(), sym); } // sqrt(2 + x) * sqrt(3 + x) == y if (eq.a is Product && (eq.a as Product).elts.All(elt => elt.Has(sym))) return eq; if (eq.a is Product) { var items = ((Product)eq.a).elts.FindAll(elt => elt.FreeOf(sym)); return IsolateVariableEq( new Equation( eq.a / Product.FromRange(items).Simplify(), eq.b / Product.FromRange(items).Simplify()), sym); } // x ^ -2 == y if (eq.a is Power && (eq.a as Power).bas == sym && (eq.a as Power).exp is Integer && ((eq.a as Power).exp as Integer).val < 0) return (eq.a / eq.a == eq.b / eq.a).IsolateVariableEq(sym); if (eq.a is Power) return eq; // sin(x) == y // Or(x == asin(y), x == Pi - asin(y)) if (eq.a is Sin) return new Or( (eq.a as Sin).args[0] == new Asin(eq.b), (eq.a as Sin).args[0] == new Symbol("Pi") - new Asin(eq.b)) .IsolateVariable(sym); // tan(x) == y // x == atan(t) if (eq.a is Tan) return ((eq.a as Tan).args[0] == new Atan(eq.b)) .IsolateVariable(sym); // asin(x) == y // // x == sin(y) if (eq.a is Asin) return ((eq.a as Asin).args[0] == new Sin(eq.b)) .IsolateVariable(sym); throw new Exception(); } public static MathObject IsolateVariable(this MathObject obj, Symbol sym) { if (obj is Or) return Or.FromRange((obj as Or).args.Select(elt => elt.IsolateVariable(sym))).Simplify(); if (obj is And) return And.FromRange((obj as And).args.Select(elt => elt.IsolateVariable(sym))).Simplify(); if (obj is Equation) return (obj as Equation).IsolateVariableEq(sym); throw new Exception(); } } } ================================================ FILE: Symbolism/LeadingCoefficientGpe.cs ================================================ using System.Collections.Generic; using Symbolism.CoefficientGpe; using Symbolism.DegreeGpe; namespace Symbolism { namespace LeadingCoefficientGpe { public static class Extensions { public static MathObject LeadingCoefficientGpe(this MathObject u, MathObject x) => u.CoefficientGpe(x, u.DegreeGpe(new List() { x })); } } } ================================================ FILE: Symbolism/LogicalExpand.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace Symbolism.LogicalExpand { public static class Extensions { public static MathObject LogicalExpand(this MathObject obj) { if (obj is Or) { return (obj as Or).Map(elt => elt.LogicalExpand()); } if (obj is And && (obj as And).args.Any(elt => elt is Or) && (obj as And).args.Count() > 1) { var before = new List(); Or or = null; var after = new List(); foreach (var elt in (obj as And).args) { if (elt is Or && or == null) or = elt as Or; else if (or == null) before.Add(elt); else after.Add(elt); } return or.Map(or_elt => new And( And.FromRange(before).Simplify().LogicalExpand(), or_elt, And.FromRange(after).Simplify().LogicalExpand()).Simplify()).LogicalExpand(); } return obj; } } } ================================================ FILE: Symbolism/PolynomialDivision.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using Symbolism.DegreeGpe; using Symbolism.LeadingCoefficientGpe; using Symbolism.AlgebraicExpand; namespace Symbolism { namespace PolynomialDivision { public static class Extensions { public static (MathObject quotient, MathObject remainder) PolynomialDivision(MathObject u, MathObject v, MathObject x) { var q = (MathObject)0; var r = u; var m = r.DegreeGpe(new List { x }); var n = v.DegreeGpe(new List { x }); var lcv = v.LeadingCoefficientGpe(x); while (m >= n && r != 0) { var lcr = r.LeadingCoefficientGpe(x); var s = lcr / lcv; q = q + s * (x ^ (m - n)); r = ((r - (lcr * (x ^ m))) - (v - lcv * (x ^ n)) * s * (x ^ (m - n))).AlgebraicExpand(); m = r.DegreeGpe(new List { x }); } return (q, r); } } } } ================================================ FILE: Symbolism/PolynomialGcd.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using Symbolism.LeadingCoefficientGpe; using Symbolism.AlgebraicExpand; namespace Symbolism { namespace PolynomialGcd { using static Symbolism.PolynomialDivision.Extensions; public static class Extensions { public static MathObject PolynomialGcd(MathObject u, MathObject v, MathObject x) { if (u == 0 && v == 0) return 0; var u_ = u; var v_ = v; while (true) { if (v_ == 0) return (u_ / u_.LeadingCoefficientGpe(x)).AlgebraicExpand(); (u_, v_) = (v_, PolynomialDivision(u_, v_, x).remainder); } } } } } ================================================ FILE: Symbolism/RationalExpand.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using Symbolism.AlgebraicExpand; using Symbolism.RationalizeExpression; namespace Symbolism { namespace RationalExpand { public static class Extensions { public static MathObject RationalExpand(this MathObject u) { var f = u.Numerator().AlgebraicExpand(); var g = u.Denominator().AlgebraicExpand(); if (g == 0) return false; var h = (f / g).RationalizeExpression(); if (h == u) return u; return h.RationalExpand(); } } } } ================================================ FILE: Symbolism/RationalizeExpression.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace Symbolism { namespace RationalizeExpression { public static class Extensions { static MathObject RationalizeSum(MathObject u, MathObject v) { var m = u.Numerator(); var r = u.Denominator(); var n = v.Numerator(); var s = v.Denominator(); if (r == 1 && s == 1) return u + v; return RationalizeSum(m * s, n * r) / (r * s); } public static MathObject RationalizeExpression(this MathObject u) { if (u is Equation) return new Equation( (u as Equation).a.RationalizeExpression(), (u as Equation).b.RationalizeExpression(), (u as Equation).Operator); if (u is Power) return (u as Power).bas.RationalizeExpression() ^ (u as Power).exp; if (u is Product) return (u as Product).Map(elt => elt.RationalizeExpression()); if (u is Sum) { var f = (u as Sum).elts[0]; var g = f.RationalizeExpression(); var r = (u - f).RationalizeExpression(); return RationalizeSum(g, r); } return u; } } } } ================================================ FILE: Symbolism/SimplifyEquation.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace Symbolism.SimplifyEquation { public static class Extensions { public static MathObject SimplifyEquation(this MathObject expr) { // 10 * x == 0 -> x == 0 // 10 * x != 0 -> x == 0 if (expr is Equation && (expr as Equation).a is Product && ((expr as Equation).a as Product).elts.Any(elt => elt is Number) && ((expr as Equation).b == 0)) return new Equation( Product.FromRange(((expr as Equation).a as Product).elts.Where(elt => !(elt is Number))).Simplify(), 0, (expr as Equation).Operator).Simplify(); // x ^ 2 == 0 -> x == 0 // x ^ 2 != 0 -> x == 0 if (expr is Equation && (expr as Equation).b == 0 && (expr as Equation).a is Power && ((expr as Equation).a as Power).exp is Integer && (((expr as Equation).a as Power).exp as Integer).val > 0) return ((expr as Equation).a as Power).bas == 0; if (expr is And) return (expr as And).Map(elt => elt.SimplifyEquation()); if (expr is Or) return (expr as Or).Map(elt => elt.SimplifyEquation()); return expr; } } } ================================================ FILE: Symbolism/SimplifyLogical.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace Symbolism.SimplifyLogical { public static class Extensions { static bool HasDuplicates(this IEnumerable ls) { foreach (var elt in ls) if (ls.Count(item => item.Equals(elt)) > 1) return true; return false; } static IEnumerable RemoveDuplicates(this IEnumerable seq) { var ls = new List(); foreach (var elt in seq) if (ls.Any(item => item.Equals(elt)) == false) ls.Add(elt); return ls; } public static MathObject SimplifyLogical(this MathObject expr) { if (expr is And && (expr as And).args.HasDuplicates()) return And.FromRange((expr as And).args.RemoveDuplicates()); if (expr is Or && (expr as Or).args.HasDuplicates()) return Or.FromRange((expr as Or).args.RemoveDuplicates()) .SimplifyLogical(); if (expr is Or) return (expr as Or).Map(elt => elt.SimplifyLogical()); return expr; } } } ================================================ FILE: Symbolism/Substitute.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace Symbolism { namespace Substitute { public static class Extensions { public static MathObject Substitute(this MathObject obj, MathObject a, MathObject b) { if (obj == a) return b; if (obj is Equation) { if ((obj as Equation).Operator == Equation.Operators.Equal) return ((obj as Equation).a.Substitute(a, b) == (obj as Equation).b.Substitute(a, b)).Simplify(); if ((obj as Equation).Operator == Equation.Operators.NotEqual) return ((obj as Equation).a.Substitute(a, b) != (obj as Equation).b.Substitute(a, b)).Simplify(); if ((obj as Equation).Operator == Equation.Operators.LessThan) return ((obj as Equation).a.Substitute(a, b) < (obj as Equation).b.Substitute(a, b)).Simplify(); if ((obj as Equation).Operator == Equation.Operators.GreaterThan) return ((obj as Equation).a.Substitute(a, b) > (obj as Equation).b.Substitute(a, b)).Simplify(); throw new Exception(); } if (obj is Power) return (obj as Power).bas.Substitute(a, b) ^ (obj as Power).exp.Substitute(a, b); if (obj is Product) return (obj as Product).Map(elt => elt.Substitute(a, b)); if (obj is Sum) return (obj as Sum).Map(elt => elt.Substitute(a, b)); if (obj is Function) { var obj_ = obj as Function; return new Function( obj_.name, obj_.proc, obj_.args.ConvertAll(arg => arg.Substitute(a, b))) .Simplify(); } return obj; } public static MathObject SubstituteEq(this MathObject obj, Equation eq) => obj.Substitute(eq.a, eq.b); public static MathObject SubstituteEqLs(this MathObject obj, List eqs) => eqs.Aggregate(obj, (a, eq) => a.SubstituteEq(eq)); public static MathObject Substitute(this MathObject obj, MathObject a, int b) => obj.Substitute(a, new Integer(b)); public static MathObject Substitute(this MathObject obj, MathObject a, double b) => obj.Substitute(a, new DoubleFloat(b)); } } } ================================================ FILE: Symbolism/Symbolism.cs ================================================ /* Copyright 2013 Eduardo Cavazos Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ using System; using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; using System.Numerics; using static Symbolism.ListConstructor; namespace Symbolism { public abstract class MathObject { ////////////////////////////////////////////////////////////////////// public static implicit operator MathObject(int n) => new Integer(n); public static implicit operator MathObject(BigInteger n) => new Integer(n); public static implicit operator MathObject(bool val) => new Bool(val); public static implicit operator MathObject(double val) => new DoubleFloat(val); ////////////////////////////////////////////////////////////////////// #region overloads for 'int' public static MathObject operator +(MathObject a, int b) => a + new Integer(b); public static MathObject operator -(MathObject a, int b) => a - new Integer(b); public static MathObject operator *(MathObject a, int b) => a * new Integer(b); public static MathObject operator /(MathObject a, int b) => a / new Integer(b); public static MathObject operator ^(MathObject a, int b) => a ^ new Integer(b); public static MathObject operator +(int a, MathObject b) => new Integer(a) + b; public static MathObject operator -(int a, MathObject b) => new Integer(a) - b; public static MathObject operator *(int a, MathObject b) => new Integer(a) * b; public static MathObject operator /(int a, MathObject b) => new Integer(a) / b; public static MathObject operator ^(int a, MathObject b) => new Integer(a) ^ b; #endregion ////////////////////////////////////////////////////////////////////// #region overloads for 'BigInteger' public static MathObject operator +(MathObject a, BigInteger b) => a + new Integer(b); public static MathObject operator -(MathObject a, BigInteger b) => a - new Integer(b); public static MathObject operator *(MathObject a, BigInteger b) => a * new Integer(b); public static MathObject operator /(MathObject a, BigInteger b) => a / new Integer(b); public static MathObject operator ^(MathObject a, BigInteger b) => a ^ new Integer(b); public static MathObject operator +(BigInteger a, MathObject b) => new Integer(a) + b; public static MathObject operator -(BigInteger a, MathObject b) => new Integer(a) - b; public static MathObject operator *(BigInteger a, MathObject b) => new Integer(a) * b; public static MathObject operator /(BigInteger a, MathObject b) => new Integer(a) / b; public static MathObject operator ^(BigInteger a, MathObject b) => new Integer(a) ^ b; #endregion ////////////////////////////////////////////////////////////////////// #region overloads for 'double' public static MathObject operator +(MathObject a, double b) => a + new DoubleFloat(b); public static MathObject operator -(MathObject a, double b) => a - new DoubleFloat(b); public static MathObject operator *(MathObject a, double b) => a * new DoubleFloat(b); public static MathObject operator /(MathObject a, double b) => a / new DoubleFloat(b); public static MathObject operator ^(MathObject a, double b) => a ^ new DoubleFloat(b); public static MathObject operator +(double a, MathObject b) => new DoubleFloat(a) + b; public static MathObject operator -(double a, MathObject b) => new DoubleFloat(a) - b; public static MathObject operator *(double a, MathObject b) => new DoubleFloat(a) * b; public static MathObject operator /(double a, MathObject b) => new DoubleFloat(a) / b; public static MathObject operator ^(double a, MathObject b) => new DoubleFloat(a) ^ b; #endregion ////////////////////////////////////////////////////////////////////// public static Equation operator ==(MathObject a, MathObject b) => new Equation(a, b); public static Equation operator !=(MathObject a, MathObject b) => new Equation(a, b, Equation.Operators.NotEqual); public static Equation operator <(MathObject a, MathObject b) => new Equation(a, b, Equation.Operators.LessThan); public static Equation operator >(MathObject a, MathObject b) => new Equation(a, b, Equation.Operators.GreaterThan); public static Equation operator ==(MathObject a, double b) => new Equation(a, new DoubleFloat(b)); public static Equation operator ==(double a, MathObject b) => new Equation(new DoubleFloat(a), b); public static Equation operator !=(MathObject a, double b) => new Equation(a, new DoubleFloat(b), Equation.Operators.NotEqual); public static Equation operator !=(double a, MathObject b) => new Equation(new DoubleFloat(a), b, Equation.Operators.NotEqual); public static Equation operator ==(MathObject a, int b) => new Equation(a, new Integer(b)); public static Equation operator ==(int a, MathObject b) => new Equation(new Integer(a), b); public static Equation operator !=(MathObject a, int b) => new Equation(a, new Integer(b), Equation.Operators.NotEqual); public static Equation operator !=(int a, MathObject b) => new Equation(new Integer(a), b, Equation.Operators.NotEqual); ////////////////////////////////////////////////////////////////////// public static MathObject operator +(MathObject a, MathObject b) => new Sum(a, b).Simplify(); public static MathObject operator -(MathObject a, MathObject b) => new Difference(a, b).Simplify(); public static MathObject operator *(MathObject a, MathObject b) => new Product(a, b).Simplify(); public static MathObject operator /(MathObject a, MathObject b) => new Quotient(a, b).Simplify(); public static MathObject operator ^(MathObject a, MathObject b) => new Power(a, b).Simplify(); public static MathObject operator -(MathObject a) { return new Difference(a).Simplify(); } // Precedence is used for printing purposes. // Thus, the precedence values below do not necessarily reflect // the C# operator precedence values. // For example, in C#, the precedence of ^ is lower than +. // But for printing purposes, we'd like ^ to have a // higher precedence than +. public int Precedence() { if (this is Integer) return 1000; if (this is DoubleFloat) return 1000; if (this is Symbol) return 1000; if (this is Function) return 1000; if (this is Fraction) return 1000; if (this is Power) return 130; if (this is Product) return 120; if (this is Sum) return 110; Console.WriteLine(this.GetType().Name); throw new Exception(); } public enum ToStringForms { Full, Standard } public static ToStringForms ToStringForm = ToStringForms.Full; public virtual string FullForm() => base.ToString(); public virtual string StandardForm() => FullForm(); public override string ToString() { if (ToStringForm == ToStringForms.Full) return FullForm(); if (ToStringForm == ToStringForms.Standard) return StandardForm(); throw new Exception(); } public virtual MathObject Numerator() => this; public virtual MathObject Denominator() => 1; public override bool Equals(object obj) { throw new Exception("MathObject.Equals called - abstract class"); } public override int GetHashCode() => base.GetHashCode(); } public class Equation : MathObject { public enum Operators { Equal, NotEqual, LessThan, GreaterThan } public readonly MathObject a; public readonly MathObject b; public Operators Operator; public Equation(MathObject x, MathObject y) { a = x; b = y; Operator = Operators.Equal; } public Equation(MathObject x, MathObject y, Operators op) { a = x; b = y; Operator = op; } public override string FullForm() { if (Operator == Operators.Equal) return a + " == " + b; if (Operator == Operators.NotEqual) return a + " != " + b; if (Operator == Operators.LessThan) return a + " < " + b; if (Operator == Operators.GreaterThan) return a + " > " + b; throw new Exception(); } public override bool Equals(object obj) => obj is Equation && a.Equals((obj as Equation).a) && b.Equals((obj as Equation).b) && Operator == (obj as Equation).Operator; Boolean ToBoolean() { if (a is Bool && b is Bool) return (a as Bool).Equals(b); if (a is Equation && b is Equation) return (a as Equation).Equals(b); if (a is Integer && b is Integer) return ((Integer)a).Equals(b); if (a is DoubleFloat && b is DoubleFloat) return ((DoubleFloat)a).Equals(b); if (a is Symbol && b is Symbol) return ((Symbol)a).Equals(b); if (a is Sum && b is Sum) return ((Sum)a).Equals(b); if (a is Product && b is Product) return ((Product)a).Equals(b); if (a is Fraction && b is Fraction) return ((Fraction)a).Equals(b); if (a is Power && b is Power) return ((Power)a).Equals(b); if (a is Function && b is Function) return ((Function)a).Equals(b); if ((((object)a) == null) && (((object)b) == null)) return true; if (((object)a) == null) return false; if (((object)b) == null) return false; if (a.GetType() != b.GetType()) return false; Console.WriteLine("" + a.GetType() + " " + b.GetType()); throw new Exception(); } public static implicit operator Boolean(Equation eq) { if (eq.Operator == Operators.Equal) return (eq.a == eq.b).ToBoolean(); if (eq.Operator == Operators.NotEqual) return !((eq.a == eq.b).ToBoolean()); if (eq.Operator == Operators.LessThan) if (eq.a is Number && eq.b is Number) return (eq.a as Number).ToDouble().val < (eq.b as Number).ToDouble().val; if (eq.Operator == Operators.GreaterThan) if (eq.a is Number && eq.b is Number) return (eq.a as Number).ToDouble().val > (eq.b as Number).ToDouble().val; throw new Exception(); } public MathObject Simplify() { if (a is Number && b is Number) return (bool)this; return this; } public override int GetHashCode() => new { a, b }.GetHashCode(); } public class Bool : MathObject { public readonly bool val; public Bool(bool b) { val = b; } public override string FullForm() => val.ToString(); public override bool Equals(object obj) => val == (obj as Bool)?.val; public override int GetHashCode() => val.GetHashCode(); } //public class NotEqual //{ // public MathObject a; // public MathObject b; // public NotEqual(MathObject x, MathObject y) // { a = x; b = y; } // public static implicit operator Boolean(NotEqual eq) // { return !((eq.a == eq.b).ToBoolean()); } //} public abstract class Number : MathObject { public abstract DoubleFloat ToDouble(); } public class Integer : Number { public readonly BigInteger val; public Integer(int n) { val = n; } public Integer(BigInteger n) { val = n; } public static implicit operator Integer(BigInteger n) => new Integer(n); // public static MathObject operator *(MathObject a, MathObject b) => new Product(a, b).Simplify(); public static Integer operator +(Integer a, Integer b) => a.val + b.val; public static Integer operator -(Integer a, Integer b) => a.val - b.val; public static Integer operator *(Integer a, Integer b) => a.val * b.val; public override string FullForm() => val.ToString(); public override bool Equals(object obj) => val == (obj as Integer)?.val; public override int GetHashCode() => val.GetHashCode(); public override DoubleFloat ToDouble() => new DoubleFloat((double)val); } public class DoubleFloat : Number { public static double? tolerance; public readonly double val; public DoubleFloat(double n) { val = n; } public override string FullForm() => val.ToString("R"); //public bool EqualWithinTolerance(DoubleFloat obj) //{ // if (tolerance.HasValue) // return Math.Abs(val - obj.val) < tolerance; // throw new Exception(); //} public override bool Equals(object obj) { if (obj is DoubleFloat && tolerance.HasValue) return Math.Abs(val - (obj as DoubleFloat).val) < tolerance; if (obj is DoubleFloat) return val == ((DoubleFloat)obj).val; return false; } public override int GetHashCode() => val.GetHashCode(); public override DoubleFloat ToDouble() => this; } public class Fraction : Number { public readonly Integer numerator; public readonly Integer denominator; public Fraction(Integer a, Integer b) { numerator = a; denominator = b; } public override string FullForm() => numerator + "/" + denominator; public override DoubleFloat ToDouble() => new DoubleFloat((double)numerator.val / (double)denominator.val); ////////////////////////////////////////////////////////////////////// public override bool Equals(object obj) => numerator == (obj as Fraction)?.numerator && denominator == (obj as Fraction)?.denominator; public override int GetHashCode() => new { numerator, denominator }.GetHashCode(); public override MathObject Numerator() => numerator; public override MathObject Denominator() => denominator; } public static class Rational { static BigInteger Div(BigInteger a, BigInteger b) { BigInteger rem; return BigInteger.DivRem(a, b, out rem); } static BigInteger Rem(BigInteger a, BigInteger b) { BigInteger rem; BigInteger.DivRem(a, b, out rem); return rem; } static BigInteger Gcd(BigInteger a, BigInteger b) { BigInteger r; while (b != 0) { r = Rem(a, b); a = b; b = r; } return BigInteger.Abs(a); } public static MathObject SimplifyRationalNumber(MathObject u) { if (u is Integer) return u; if (u is Fraction) { var u_ = (Fraction)u; var n = u_.numerator.val; var d = u_.denominator.val; if (Rem(n, d) == 0) return Div(n, d); var g = Gcd(n, d); if (d > 0) return new Fraction(Div(n, g), Div(d, g)); if (d < 0) return new Fraction(Div(-n, g), Div(-d, g)); } throw new Exception(); } public static Integer Numerator(MathObject u) { // (a / b) / (c / d) // (a / b) * (d / c) // (a * d) / (b * c) if (u is Integer) return (Integer)u; if (u is Fraction) { var u_ = u as Fraction; //return // Numerator(u_.numerator).val // * // Denominator(u_.denominator).val; return Numerator(u_.numerator) * Denominator(u_.denominator); } throw new Exception(); } public static Integer Denominator(MathObject u) { // (a / b) / (c / d) // (a / b) * (d / c) // (a * d) / (b * c) if (u is Integer) return new Integer(1); if (u is Fraction) { var u_ = u as Fraction; return Denominator(u_.numerator) * Numerator(u_.denominator); } throw new Exception(); } public static Fraction EvaluateSum(MathObject v, MathObject w) => // a / b + c / d // a d / b d + c b / b d // (a d + c b) / (b d) new Fraction( Numerator(v) * Denominator(w) + Numerator(w) * Denominator(v), Denominator(v) * Denominator(w)); public static Fraction EvaluateDifference(MathObject v, MathObject w) => new Fraction( Numerator(v) * Denominator(w) - Numerator(w) * Denominator(v), Denominator(v) * Denominator(w)); public static Fraction EvaluateProduct(MathObject v, MathObject w) => new Fraction( Numerator(v) * Numerator(w), Denominator(v) * Denominator(w)); public static MathObject EvaluateQuotient(MathObject v, MathObject w) { if (Numerator(w).val == 0) return new Undefined(); return new Fraction( Numerator(v) * Denominator(w), Numerator(w) * Denominator(v)); } public static MathObject EvaluatePower(MathObject v, BigInteger n) { if (Numerator(v).val != 0) { if (n > 0) return EvaluateProduct(EvaluatePower(v, n - 1), v); if (n == 0) return 1; if (n == -1) return new Fraction(Denominator(v), Numerator(v)); if (n < -1) { var s = new Fraction(Denominator(v), Numerator(v)); return EvaluatePower(s, -n); } } if (n >= 1) return 0; if (n <= 0) return new Undefined(); throw new Exception(); } public static MathObject SimplifyRNERec(MathObject u) { if (u is Integer) return u; if (u is Fraction) if (Denominator((Fraction)u).val == 0) return new Undefined(); else return u; if (u is Sum && ((Sum)u).elts.Count == 1) { return SimplifyRNERec(((Sum)u).elts[0]); } if (u is Difference && ((Difference)u).elts.Count == 1) { var v = SimplifyRNERec(((Difference)u).elts[0]); if (v == new Undefined()) return v; return EvaluateProduct(-1, v); } if (u is Sum && ((Sum)u).elts.Count == 2) { var v = SimplifyRNERec(((Sum)u).elts[0]); var w = SimplifyRNERec(((Sum)u).elts[1]); if (v == new Undefined() || w == new Undefined()) return new Undefined(); return EvaluateSum(v, w); } if (u is Product && ((Product)u).elts.Count == 2) { var v = SimplifyRNERec(((Product)u).elts[0]); var w = SimplifyRNERec(((Product)u).elts[1]); if (v == new Undefined() || w == new Undefined()) return new Undefined(); return EvaluateProduct(v, w); } if (u is Difference && ((Difference)u).elts.Count == 2) { var v = SimplifyRNERec(((Difference)u).elts[0]); var w = SimplifyRNERec(((Difference)u).elts[1]); if (v == new Undefined() || w == new Undefined()) return new Undefined(); return EvaluateDifference(v, w); } if (u is Fraction) { var v = SimplifyRNERec(((Fraction)u).numerator); var w = SimplifyRNERec(((Fraction)u).denominator); if (v == new Undefined() || w == new Undefined()) return new Undefined(); return EvaluateQuotient(v, w); } if (u is Power) { var v = SimplifyRNERec(((Power)u).bas); if (v == new Undefined()) return v; return EvaluatePower(v, ((Integer)((Power)u).exp).val); } throw new Exception(); } public static MathObject SimplifyRNE(MathObject u) { var v = SimplifyRNERec(u); if (v is Undefined) return v; return SimplifyRationalNumber(v); } } public class Undefined : MathObject { } public static class MiscUtils { } public class Symbol : MathObject { public readonly String name; public Symbol(String str) { name = str; } public override string FullForm() => name; public override int GetHashCode() => name.GetHashCode(); public override bool Equals(Object obj) => obj is Symbol ? name == (obj as Symbol).name : false; } public static class ListConstructor { public static List List(params T[] items) => new List(items); public static ImmutableList ImList(params T[] items) => ImmutableList.Create(items); } public static class ListUtils { public static ImmutableList Cons(this ImmutableList obj, MathObject elt) => obj.Insert(0, elt); public static ImmutableList Cdr(this ImmutableList obj) => obj.RemoveAt(0); public static bool equal(ImmutableList a, ImmutableList b) { if (a.Count == 0 && b.Count == 0) return true; if (a.Count == 0) return false; if (b.Count == 0) return false; if (a[0] == b[0]) return equal(a.Cdr(), b.Cdr()); return false; } } public class Function : MathObject { public delegate MathObject Proc(params MathObject[] ls); public readonly String name; public readonly Proc proc; public readonly ImmutableList args; public Function(string name, Proc proc, IEnumerable args) { this.name = name; this.proc = proc; this.args = ImmutableList.CreateRange(args); } public override bool Equals(object obj) => GetType() == obj.GetType() && name == (obj as Function).name && ListUtils.equal(args, ((Function)obj).args); public MathObject Simplify() => proc == null ? this : proc(args.ToArray()); public override string FullForm() => $"{name}({string.Join(", ", args)})"; public MathObject Clone() => MemberwiseClone() as MathObject; public override int GetHashCode() => new { name, args }.GetHashCode(); } public static class FunctionExtensions { //public static MathObject Map(this T obj, Func proc) where T : Function, new() //{ // // return new T() { args = obj.args.Select(proc).ToList() }.Simplify(); // // return //} } public class And : Function { static MathObject AndProc(MathObject[] ls) { if (ls.Count() == 0) return true; if (ls.Count() == 1) return ls.First(); if (ls.Any(elt => elt == false)) return false; if (ls.Any(elt => elt == true)) return new And(ls.Where(elt => elt != true).ToArray()).Simplify(); if (ls.Any(elt => elt is And)) { var items = new List(); foreach (var elt in ls) { if (elt is And) items.AddRange((elt as And).args); else items.Add(elt); } return And.FromRange(items).Simplify(); } return new And(ls); } public And(params MathObject[] ls) : base("and", AndProc, ls) { } public And() : base("and", AndProc, new List()) { } public static And FromRange(IEnumerable ls) => new And(ls.ToArray()); public MathObject Add(MathObject obj) => And.FromRange(args.Add(obj)).Simplify(); public MathObject AddRange(IEnumerable ls) => And.FromRange(args.AddRange(ls)).Simplify(); public MathObject Map(Func proc) => And.FromRange(args.Select(proc)).Simplify(); } public class Or : Function { static MathObject OrProc(params MathObject[] ls) { if (ls.Count() == 1) return ls.First(); // 10 || false || 20 -> 10 || 20 if (ls.Any(elt => elt == false)) return Or.FromRange(ls.Where(elt => elt != false)).Simplify(); if (ls.Any(elt => (elt is Bool) && (elt as Bool).val)) return new Bool(true); if (ls.All(elt => (elt is Bool) && (elt as Bool).val == false)) return new Bool(false); if (ls.Any(elt => elt is Or)) { var items = new List(); foreach (var elt in ls) { if (elt is Or) items.AddRange((elt as Or).args); else items.Add(elt); } return Or.FromRange(items).Simplify(); } return new Or(ls); } public Or(params MathObject[] ls) : base("or", OrProc, ls) { } public Or() : base("or", OrProc, new List()) { } public static Or FromRange(IEnumerable ls) => new Or(ls.ToArray()); public MathObject Map(Func proc) => Or.FromRange(args.Select(proc)).Simplify(); } public static class OrderRelation { public static MathObject Base(MathObject u) => u is Power ? (u as Power).bas : u; public static MathObject Exponent(MathObject u) => u is Power ? (u as Power).exp : 1; public static MathObject Term(this MathObject u) { if (u is Product && ((Product)u).elts[0] is Number) return Product.FromRange((u as Product).elts.Cdr()); // return (u as Product).Cdr() if (u is Product) return u; return new Product(u); } public static MathObject Const(this MathObject u) => (u is Product && (u as Product).elts[0] is Number) ? (u as Product).elts[0] : 1; public static bool O3(ImmutableList uElts, ImmutableList vElts) { if (uElts.IsEmpty) return true; if (vElts.IsEmpty) return false; var u = uElts.First(); var v = vElts.First(); return (!(u == v)) ? Compare(u, v) : O3(uElts.Cdr(), vElts.Cdr()); } public static bool Compare(MathObject u, MathObject v) { if (u is DoubleFloat && v is DoubleFloat) return ((DoubleFloat)u).val < ((DoubleFloat)v).val; // if (u is DoubleFloat && v is Integer) return ((DoubleFloat)u).val < ((Integer)v).val; if (u is DoubleFloat && v is Integer) return ((DoubleFloat)u).val < ((double)((Integer)v).val); if (u is DoubleFloat && v is Fraction) return ((DoubleFloat)u).val < ((double)((Fraction)v).numerator.val) / ((double)((Fraction)v).denominator.val); if (u is Integer && v is DoubleFloat) return ((double)((Integer)u).val) < ((DoubleFloat)v).val; if (u is Fraction && v is DoubleFloat) return ((double)((Fraction)u).numerator.val) / ((double)((Fraction)u).denominator.val) < ((DoubleFloat)v).val; if (u is Integer) return Compare(new Fraction((Integer)u, new Integer(1)), v); if (v is Integer) return Compare(u, new Fraction((Integer)v, new Integer(1))); if (u is Fraction && v is Fraction) { var u_ = (Fraction)u; var v_ = (Fraction)v; // a / b < c / d // // (a d) / (b d) < (c b) / (b d) return (u_.numerator.val * v_.denominator.val) < (v_.numerator.val * u_.denominator.val); } if (u is Symbol && v is Symbol) return String.Compare( ((Symbol)u).name, ((Symbol)v).name) < 0; if (u is Product && v is Product) return O3( (u as Product).elts.Reverse(), (v as Product).elts.Reverse()); if (u is Sum && v is Sum) return O3( (u as Sum).elts.Reverse(), (v as Sum).elts.Reverse()); if (u is Power && v is Power) { var u_ = (Power)u; var v_ = (Power)v; return (u_.bas == v_.bas) ? Compare(u_.exp, v_.exp) : Compare(u_.bas, v_.bas); } if (u is Function && v is Function) { var u_ = (Function)u; var v_ = (Function)v; return u_.name == v_.name ? O3(u_.args, v_.args) : String.Compare(u_.name, v_.name) < 0; } if (u is Number && !(v is Number)) return true; if (u is Product && (v is Power || v is Sum || v is Function || v is Symbol)) return Compare(u, new Product(v)); if (u is Power && (v is Sum || v is Function || v is Symbol)) return Compare(u, new Power(v, new Integer(1))); if (u is Sum && (v is Function || v is Symbol)) return Compare(u, new Sum(v)); if (u is Function && v is Symbol) { var u_ = (Function)u; var v_ = (Symbol)v; return u_.name == v_.name ? false : Compare(new Symbol(u_.name), v); } return !Compare(v, u); } } public class Power : MathObject { public readonly MathObject bas; public readonly MathObject exp; public Power(MathObject a, MathObject b) { bas = a; exp = b; } public override string FullForm() => string.Format("{0} ^ {1}", bas.Precedence() < Precedence() ? $"({bas})" : $"{bas}", exp.Precedence() < Precedence() ? $"({exp})" : $"{exp}"); public override string StandardForm() { // x ^ 1/2 -> sqrt(x) if (exp == new Integer(1) / new Integer(2)) return $"sqrt({bas})"; return string.Format("{0} ^ {1}", bas.Precedence() < Precedence() ? $"({bas})" : $"{bas}", exp.Precedence() < Precedence() ? $"({exp})" : $"{exp}"); } public override bool Equals(object obj) => obj is Power && bas == (obj as Power).bas && exp == (obj as Power).exp; public MathObject Simplify() { var v = bas; var w = exp; if (v == 0) return 0; if (v == 1) return 1; if (w == 0) return 1; if (w == 1) return v; // Logic from MPL/Scheme: // //if (v is Integer && w is Integer) // return // new Integer( // (int)Math.Pow(((Integer)v).val, ((Integer)w).val)); // C# doesn't have built-in rationals. So: // 1 / 3 -> 3 ^ -1 -> 0.333... -> (int)... -> 0 //if (v is Integer && w is Integer && ((Integer)w).val > 1) // return // new Integer( // (int)Math.Pow(((Integer)v).val, ((Integer)w).val)); var n = w; if ((v is Integer || v is Fraction) && n is Integer) return Rational.SimplifyRNE(new Power(v, n)); if (v is DoubleFloat && w is Integer) return new DoubleFloat(Math.Pow(((DoubleFloat)v).val, (double) ((Integer)w).val)); if (v is DoubleFloat && w is Fraction) return new DoubleFloat(Math.Pow(((DoubleFloat)v).val, ((Fraction)w).ToDouble().val)); if (v is Integer && w is DoubleFloat) return new DoubleFloat(Math.Pow((double)((Integer)v).val, ((DoubleFloat)w).val)); if (v is Fraction && w is DoubleFloat) return new DoubleFloat(Math.Pow(((Fraction)v).ToDouble().val, ((DoubleFloat)w).val)); if (v is Power && w is Integer) { return ((Power)v).bas ^ (((Power)v).exp * w); } if (v is Product && w is Integer) return (v as Product).Map(elt => elt ^ w); return new Power(v, w); } public override MathObject Numerator() { if (exp is Integer && exp < 0) return 1; if (exp is Fraction && exp < 0) return 1; return this; } public override MathObject Denominator() { if (exp is Integer && exp < 0) return this ^ -1; if (exp is Fraction && exp < 0) return this ^ -1; return 1; } public override int GetHashCode() => new { bas, exp }.GetHashCode(); } public class Product : MathObject { public readonly ImmutableList elts; public Product(params MathObject[] ls) => elts = ImmutableList.Create(ls); public static Product FromRange(IEnumerable ls) => new Product(ls.ToArray()); public override string FullForm() => string.Join(" * ", elts.ConvertAll(elt => elt.Precedence() < Precedence() ? $"({elt})" : $"{elt}")); public override string StandardForm() { if (this.Denominator() == 1) { if (this.Const() < 0 && this / this.Const() is Sum) return $"-({this * -1})"; if (this.Const() < 0) return $"-{this * -1}"; return string.Join(" * ", elts.ConvertAll(elt => elt.Precedence() < Precedence() || (elt is Power && (elt as Power).exp != new Integer(1) / 2) ? $"({elt})" : $"{elt}")); } var expr_a = this.Numerator(); var expr_b = this.Denominator(); var expr_a_ = expr_a is Sum || (expr_a is Power && (expr_a as Power).exp != new Integer(1) / 2) ? $"({expr_a})" : $"{expr_a}"; var expr_b_ = expr_b is Sum || expr_b is Product || (expr_b is Power && (expr_b as Power).exp != new Integer(1) / 2) ? $"({expr_b})" : $"{expr_b}"; return $"{expr_a_} / {expr_b_}"; } public override int GetHashCode() => elts.GetHashCode(); public override bool Equals(object obj) => obj is Product && ListUtils.equal(elts, (obj as Product).elts); static ImmutableList MergeProducts(ImmutableList pElts, ImmutableList qElts) { if (pElts.Count == 0) return qElts; if (qElts.Count == 0) return pElts; var p = pElts[0]; var ps = pElts.Cdr(); var q = qElts[0]; var qs = qElts.Cdr(); var res = RecursiveSimplify(ImList(p, q)); if (res.Count == 0) return MergeProducts(ps, qs); if (res.Count == 1) return MergeProducts(ps, qs).Cons(res[0]); if (ListUtils.equal(res, ImList(p, q))) return MergeProducts(ps, qElts).Cons(p); if (ListUtils.equal(res, ImList(q, p))) return MergeProducts(pElts, qs).Cons(q); throw new Exception(); } static ImmutableList SimplifyDoubleNumberProduct(DoubleFloat a, Number b) { double val = 0.0; if (b is DoubleFloat) val = a.val * ((DoubleFloat)b).val; if (b is Integer) val = a.val * (double)((Integer)b).val; if (b is Fraction) val = a.val * ((Fraction)b).ToDouble().val; if (val == 1.0) return ImmutableList.Create(); return ImList(new DoubleFloat(val)); } public static ImmutableList RecursiveSimplify(ImmutableList elts) { if (elts.Count == 2) { if (elts[0] is Product && elts[1] is Product) return MergeProducts( ((Product)elts[0]).elts, ((Product)elts[1]).elts); if (elts[0] is Product) return MergeProducts(((Product)elts[0]).elts, ImList(elts[1])); if (elts[1] is Product) return MergeProducts(ImList(elts[0]), ((Product)elts[1]).elts); ////////////////////////////////////////////////////////////////////// if (elts[0] is DoubleFloat && elts[1] is Number) return SimplifyDoubleNumberProduct((DoubleFloat)elts[0], (Number)elts[1]); if (elts[0] is Number && elts[1] is DoubleFloat) return SimplifyDoubleNumberProduct((DoubleFloat)elts[1], (Number)elts[0]); ////////////////////////////////////////////////////////////////////// if ((elts[0] is Integer || elts[0] is Fraction) && (elts[1] is Integer || elts[1] is Fraction)) { var P = Rational.SimplifyRNE(new Product(elts[0], elts[1])); if (P == 1) return ImmutableList.Create(); return ImList(P); } if (elts[0] == 1) return ImList(elts[1]); if (elts[1] == 1) return ImList(elts[0]); var p = elts[0]; var q = elts[1]; if (OrderRelation.Base(p) == OrderRelation.Base(q)) { var res = OrderRelation.Base(p) ^ (OrderRelation.Exponent(p) + OrderRelation.Exponent(q)); if (res == 1) return ImmutableList.Create(); return ImList(res); } if (OrderRelation.Compare(q, p)) return ImList(q, p); return ImList(p, q); } if (elts[0] is Product) return MergeProducts( ((Product)elts[0]).elts, RecursiveSimplify(elts.Cdr())); return MergeProducts( ImList(elts[0]), RecursiveSimplify(elts.Cdr())); throw new Exception(); } public MathObject Simplify() { if (elts.Count == 1) return elts[0]; if (elts.Any(elt => elt == 0)) return 0; var res = RecursiveSimplify(elts); if (res.IsEmpty) return 1; if (res.Count == 1) return res[0]; // Without the below, the following throws an exception: // sqrt(a * b) * (sqrt(a * b) / a) / c if (res.Any(elt => elt is Product)) return Product.FromRange(res).Simplify(); return Product.FromRange(res); } public override MathObject Numerator() => Product.FromRange(elts.Select(elt => elt.Numerator())).Simplify(); public override MathObject Denominator() => Product.FromRange(elts.Select(elt => elt.Denominator())).Simplify(); public MathObject Map(Func proc) => Product.FromRange(elts.Select(proc)).Simplify(); } public class Sum : MathObject { public readonly ImmutableList elts; public Sum(params MathObject[] ls) { elts = ImmutableList.Create(ls); } public static Sum FromRange(IEnumerable ls) => new Sum(ls.ToArray()); public override int GetHashCode() => elts.GetHashCode(); public override bool Equals(object obj) => obj is Sum && ListUtils.equal(elts, (obj as Sum).elts); static ImmutableList MergeSums(ImmutableList pElts, ImmutableList qElts) { if (pElts.Count == 0) return qElts; if (qElts.Count == 0) return pElts; var p = pElts[0]; var ps = pElts.Cdr(); var q = qElts[0]; var qs = qElts.Cdr(); var res = RecursiveSimplify(ImList(p, q)); if (res.Count == 0) return MergeSums(ps, qs); if (res.Count == 1) return MergeSums(ps, qs).Cons(res[0]); if (ListUtils.equal(res, ImList(p, q))) return MergeSums(ps, qElts).Cons(p); if (ListUtils.equal(res, ImList(q, p))) return MergeSums(pElts, qs).Cons(q); throw new Exception(); } static ImmutableList SimplifyDoubleNumberSum(DoubleFloat a, Number b) { double val = 0.0; if (b is DoubleFloat) val = a.val + ((DoubleFloat)b).val; if (b is Integer) val = a.val + (double)((Integer)b).val; if (b is Fraction) val = a.val + ((Fraction)b).ToDouble().val; if (val == 0.0) return ImmutableList.Create(); return ImmutableList.Create(new DoubleFloat(val)); } static ImmutableList RecursiveSimplify(ImmutableList elts) { if (elts.Count == 2) { if (elts[0] is Sum && elts[1] is Sum) return MergeSums( ((Sum)elts[0]).elts, ((Sum)elts[1]).elts); if (elts[0] is Sum) return MergeSums( ((Sum)elts[0]).elts, ImList(elts[1])); if (elts[1] is Sum) return MergeSums( ImList(elts[0]), ((Sum)elts[1]).elts); ////////////////////////////////////////////////////////////////////// if (elts[0] is DoubleFloat && elts[1] is Number) return SimplifyDoubleNumberSum((DoubleFloat)elts[0], (Number)elts[1]); if (elts[0] is Number && elts[1] is DoubleFloat) return SimplifyDoubleNumberSum((DoubleFloat)elts[1], (Number)elts[0]); ////////////////////////////////////////////////////////////////////// if ((elts[0] is Integer || elts[0] is Fraction) && (elts[1] is Integer || elts[1] is Fraction)) { var P = Rational.SimplifyRNE(new Sum(elts[0], elts[1])); if (P == 0) return ImmutableList.Create(); return ImList(P); } if (elts[0] == 0) return ImList(elts[1]); if (elts[1] == 0) return ImList(elts[0]); var p = elts[0]; var q = elts[1]; if (p.Term() == q.Term()) { var res = p.Term() * (p.Const() + q.Const()); if (res == 0) return ImmutableList.Create(); return ImList(res); } if (OrderRelation.Compare(q, p)) return ImList(q, p); return ImList(p, q); } if (elts[0] is Sum) return MergeSums( ((Sum)elts[0]).elts, RecursiveSimplify(elts.Cdr())); return MergeSums( ImList(elts[0]), RecursiveSimplify(elts.Cdr())); } public MathObject Simplify() { if (elts.Count == 1) return elts[0]; var res = RecursiveSimplify(elts); if (res.Count == 0) return 0; if (res.Count == 1) return res[0]; return Sum.FromRange(res); } public override string FullForm() => String.Join(" + ", elts.ConvertAll(elt => elt.Precedence() < Precedence() ? $"({elt})" : $"{elt}")); public override string StandardForm() { var result = string.Join(" ", elts .ConvertAll(elt => { var elt_ = elt.Const() < 0 ? elt * -1 : elt; var elt__ = elt.Const() < 0 && elt_ is Sum || (elt is Power && (elt as Power).exp != new Integer(1) / 2) ? $"({elt_})" : $"{elt_}"; return elt.Const() < 0 ? $"- {elt__}" : $"+ {elt__}"; })); if (result.StartsWith("+ ")) return result.Remove(0, 2); // "+ x + y" -> "x + y" if (result.StartsWith("- ")) return result.Remove(1, 1); // "- x + y" -> "-x + y" return result; } public MathObject Map(Func proc) => Sum.FromRange(elts.Select(proc)).Simplify(); } class Difference : MathObject { public readonly ImmutableList elts; public Difference(params MathObject[] ls) => elts = ImmutableList.Create(ls); public MathObject Simplify() { if (elts.Count == 1) return -1 * elts[0]; if (elts.Count == 2) return elts[0] + -1 * elts[1]; throw new Exception(); } } class Quotient : MathObject { public readonly ImmutableList elts; public Quotient(params MathObject[] ls) => elts = ImmutableList.Create(ls); public MathObject Simplify() => elts[0] * (elts[1] ^ -1); } public static class Constructors { public static MathObject sqrt(MathObject obj) => obj ^ (new Integer(1) / new Integer(2)); public static MathObject and(params MathObject[] ls) => And.FromRange(ls).Simplify(); public static MathObject or(params MathObject[] ls) => Or.FromRange(ls).Simplify(); } } ================================================ FILE: Symbolism/Symbolism.csproj ================================================ netstandard2.0 Symbolism 1.0.4 Eduardo Cavazos dharmatech Apache-2.0 Computer Algebra Library for C# https://github.com/dharmatech/Symbolism https://github.com/dharmatech/Symbolism computer-algebra;symbolic-mathematics ================================================ FILE: Symbolism/Trigonometric.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using static Symbolism.ListConstructor; using static Symbolism.Trigonometric.Constructors; namespace Symbolism.Trigonometric { public class Sin : Function { public static MathObject Mod(MathObject x, MathObject y) { if (x is Number && y is Number) { var result = Convert.ToInt32(Math.Floor(((x / y) as Number).ToDouble().val)); return x - y * result; } throw new Exception(); } static MathObject SinProc(params MathObject[] ls) { var Pi = new Symbol("Pi"); var half = new Integer(1) / 2; var u = ls[0]; if (u == 0) return 0; if (u == Pi) return 0; if (u is DoubleFloat) return new DoubleFloat(Math.Sin(((DoubleFloat)u).val)); if (u is Number && u < 0) return -sin(-u); if (u is Product && (u as Product).elts[0] is Number && (u as Product).elts[0] < 0) return -sin(-u); if (u is Product && ((u as Product).elts[0] is Integer || (u as Product).elts[0] is Fraction) && (u as Product).elts[0] > half && (u as Product).elts[1] == Pi) { var n = (u as Product).elts[0]; if (n > 2) return sin(Mod(n, 2) * Pi); if (n > 1) return -sin(n * Pi - Pi); if (n > half) return sin((1 - n) * Pi); return new Sin(n * Pi); } // sin(k/n pi) // n is one of 1 2 3 4 6 if (u is Product && List(1, 2, 3, 4, 6).Any(elt => elt == (u as Product).elts[0].Denominator()) && (u as Product).elts[0].Numerator() is Integer && (u as Product).elts[1] == Pi) { var k = (u as Product).elts[0].Numerator(); var n = (u as Product).elts[0].Denominator(); if (n == 1) return 0; if (n == 2) { if (Mod(k, 4) == 1) return 1; if (Mod(k, 4) == 3) return -1; } if (n == 3) { if (Mod(k, 6) == 1) return (3 ^ half) / 2; if (Mod(k, 6) == 2) return (3 ^ half) / 2; if (Mod(k, 6) == 4) return -(3 ^ half) / 2; if (Mod(k, 6) == 5) return -(3 ^ half) / 2; } if (n == 4) { if (Mod(k, 8) == 1) return 1 / (2 ^ half); if (Mod(k, 8) == 3) return 1 / (2 ^ half); if (Mod(k, 8) == 5) return -1 / (2 ^ half); if (Mod(k, 8) == 7) return -1 / (2 ^ half); } if (n == 6) { if (Mod(k, 12) == 1) return half; if (Mod(k, 12) == 5) return half; if (Mod(k, 12) == 7) return -half; if (Mod(k, 12) == 11) return -half; } } // sin(Pi + x + y + ...) -> -sin(x + y + ...) if (u is Sum && (u as Sum).elts.Any(elt => elt == Pi)) return -sin(u - Pi); // sin(x + n pi) Func Product_n_Pi = elt => (elt is Product) && ( (elt as Product).elts[0] is Integer || (elt as Product).elts[0] is Fraction ) && Math.Abs(((elt as Product).elts[0] as Number).ToDouble().val) >= 2.0 && (elt as Product).elts[1] == Pi; if (u is Sum && (u as Sum).elts.Any(Product_n_Pi)) { var pi_elt = (u as Sum).elts.First(Product_n_Pi); var n = (pi_elt as Product).elts[0]; return sin((u - pi_elt) + Mod(n, 2) * Pi); } // sin(a + b + ... + n/2 * Pi) Func Product_n_div_2_Pi = elt => elt is Product && ( (elt as Product).elts[0] is Integer || (elt as Product).elts[0] is Fraction ) && (elt as Product).elts[0].Denominator() == 2 && (elt as Product).elts[1] == Pi; if (u is Sum && (u as Sum).elts.Any(Product_n_div_2_Pi)) { var n_div_2_Pi = (u as Sum).elts.First(Product_n_div_2_Pi); var other_elts = u - n_div_2_Pi; var n = (n_div_2_Pi as Product).elts[0].Numerator(); if (Mod(n, 4) == 1) return new Cos(other_elts); if (Mod(n, 4) == 3) return -new Cos(other_elts); } return new Sin(u); } public Sin(MathObject param) : base("sin", SinProc, new[] { param }) { } } public class Cos : Function { public static MathObject Mod(MathObject x, MathObject y) { if (x is Number && y is Number) { var result = Convert.ToInt32(Math.Floor(((x / y) as Number).ToDouble().val)); return x - y * result; } throw new Exception(); } static MathObject CosProc(params MathObject[] ls) { var Pi = new Symbol("Pi"); var half = new Integer(1) / 2; var u = ls[0]; if (ls[0] == 0) return 1; if (ls[0] == new Symbol("Pi")) return -1; if (ls[0] is DoubleFloat) return new DoubleFloat(Math.Cos(((DoubleFloat)ls[0]).val)); if (ls[0] is Number && ls[0] < 0) return new Cos(-ls[0]); if (ls[0] is Product && (ls[0] as Product).elts[0] is Number && ((ls[0] as Product).elts[0] as Number) < 0) return new Cos(-ls[0]).Simplify(); // cos(a/b * Pi) // a/b > 1/2 if (ls[0] is Product && ( (ls[0] as Product).elts[0] is Integer || (ls[0] as Product).elts[0] is Fraction ) && ((ls[0] as Product).elts[0] as Number) > new Integer(1) / 2 && (ls[0] as Product).elts[1] == Pi ) { var n = (ls[0] as Product).elts[0]; if (n > 2) return cos(Mod(n, 2) * Pi); if (n > 1) return -cos(n * Pi - Pi); if (n > half) return -cos(Pi - n * Pi); return new Cos(n * Pi); } // cos(k/n Pi) // n is one of 1 2 3 4 6 if (ls[0] is Product && List(1, 2, 3, 4, 6) .Any(elt => elt == (ls[0] as Product).elts[0].Denominator()) && (ls[0] as Product).elts[0].Numerator() is Integer && (ls[0] as Product).elts[1] == Pi ) { var k = (ls[0] as Product).elts[0].Numerator(); var n = (ls[0] as Product).elts[0].Denominator(); if (n == 1) { if (Mod(k, 2) == 1) return -1; if (Mod(k, 2) == 0) return 1; } if (n == 2) { if (Mod(k, 2) == 1) return 0; } if (n == 3) { if (Mod(k, 6) == 1) return half; if (Mod(k, 6) == 5) return half; if (Mod(k, 6) == 2) return -half; if (Mod(k, 6) == 4) return -half; } if (n == 4) { if (Mod(k, 8) == 1) return 1 / (2 ^ half); if (Mod(k, 8) == 7) return 1 / (2 ^ half); if (Mod(k, 8) == 3) return -1 / (2 ^ half); if (Mod(k, 8) == 5) return -1 / (2 ^ half); } if (n == 6) { if (Mod(k, 12) == 1) return (3 ^ half) / 2; if (Mod(k, 12) == 11) return (3 ^ half) / 2; if (Mod(k, 12) == 5) return -(3 ^ half) / 2; if (Mod(k, 12) == 7) return -(3 ^ half) / 2; } } // cos(Pi + x + y + ...) -> -cos(x + y + ...) if (u is Sum && (u as Sum).elts.Any(elt => elt == Pi)) return -cos(u - Pi); // cos(n Pi + x + y) // n * Pi where n is Exact && abs(n) >= 2 Func Product_n_Pi = elt => (elt is Product) && ( (elt as Product).elts[0] is Integer || (elt as Product).elts[0] is Fraction ) && Math.Abs(((elt as Product).elts[0] as Number).ToDouble().val) >= 2.0 && (elt as Product).elts[1] == Pi; if (ls[0] is Sum && (ls[0] as Sum).elts.Any(Product_n_Pi)) { var pi_elt = (ls[0] as Sum).elts.First(Product_n_Pi); var n = (pi_elt as Product).elts[0]; return cos((ls[0] - pi_elt) + Mod(n, 2) * Pi); } Func Product_n_div_2_Pi = elt => elt is Product && ( (elt as Product).elts[0] is Integer || (elt as Product).elts[0] is Fraction ) && (elt as Product).elts[0].Denominator() == 2 && (elt as Product).elts[1] == Pi; // cos(a + b + ... + n/2 * Pi) -> sin(a + b + ...) if (ls[0] is Sum && (ls[0] as Sum).elts.Any(Product_n_div_2_Pi)) { var n_div_2_Pi = (ls[0] as Sum).elts.First(Product_n_div_2_Pi); var other_elts = ls[0] - n_div_2_Pi; var n = (n_div_2_Pi as Product).elts[0].Numerator(); if (Mod(n, 4) == 1) return -new Sin(other_elts); if (Mod(n, 4) == 3) return new Sin(other_elts); } return new Cos(ls[0]); } public Cos(MathObject param) : base("cos", CosProc, new[] { param }) { } } public class Tan : Function { static MathObject TanProc(params MathObject[] ls) { if (ls[0] is DoubleFloat) return new DoubleFloat(Math.Tan(((DoubleFloat)ls[0]).val)); return new Tan(ls[0]); } public Tan(MathObject param) : base("tan", TanProc, new[] { param }) { } } public class Asin : Function { static MathObject AsinProc(params MathObject[] ls) { if (ls[0] is DoubleFloat) return new DoubleFloat(Math.Asin(((DoubleFloat)ls[0]).val)); return new Asin(ls[0]); } public Asin(MathObject param) : base("asin", AsinProc, new[] { param }) { } } public class Atan : Function { static MathObject AtanProc(params MathObject[] ls) { if (ls[0] is DoubleFloat) return new DoubleFloat(Math.Atan(((DoubleFloat)ls[0]).val)); return new Atan(ls[0]); } public Atan(MathObject param) : base("atan", AtanProc, new[] { param }) { } } public class Atan2 : Function { static MathObject Atan2Proc(params MathObject[] ls) { //if ( // (ls[0] is DoubleFloat || ls[0] is Integer) // && // (ls[1] is DoubleFloat || ls[1] is Integer) // ) // return new DoubleFloat( // Math.Atan2( // (ls[0] as Number).ToDouble().val, // (ls[1] as Number).ToDouble().val)); if (ls[0] is DoubleFloat && ls[1] is DoubleFloat) return new DoubleFloat( Math.Atan2( ((DoubleFloat)ls[0]).val, ((DoubleFloat)ls[1]).val)); if (ls[0] is Integer && ls[1] is DoubleFloat) return new DoubleFloat( Math.Atan2( (double)((Integer)ls[0]).val, ((DoubleFloat)ls[1]).val)); if (ls[0] is DoubleFloat && ls[1] is Integer) return new DoubleFloat( Math.Atan2( ((DoubleFloat)ls[0]).val, (double)((Integer)ls[1]).val)); if (ls[0] is Integer && ls[1] is Integer) return new DoubleFloat( Math.Atan2( (double)((Integer)ls[0]).val, (double)((Integer)ls[1]).val)); return new Atan2(ls[0], ls[1]); } public Atan2(MathObject a, MathObject b) : base("atan2", Atan2Proc, new[] { a, b }) { } } public static class Constructors { public static MathObject sin(MathObject obj) => new Sin(obj).Simplify(); public static MathObject cos(MathObject obj) => new Cos(obj).Simplify(); public static MathObject tan(MathObject obj) => new Tan(obj).Simplify(); public static MathObject asin(MathObject obj) => new Asin(obj).Simplify(); public static MathObject atan(MathObject obj) => new Atan(obj).Simplify(); } public static class Extensions { public static Symbol Pi = new Symbol("Pi"); public static MathObject ToRadians(this MathObject n) => n * Pi / 180; public static MathObject ToDegrees(this MathObject n) => 180 * n / Pi; public static MathObject ToRadians(this int n) => new Integer(n) * Pi / 180; public static MathObject ToDegrees(this int n) => 180 * new Integer(n) / Pi; // (Integer) 180 * n / Pi } } ================================================ FILE: Symbolism/Utils.cs ================================================ using System; using System.Collections.Generic; using System.Text; namespace Symbolism.Utils { public static class Extensions { public static T Disp(this T obj) { Console.WriteLine(obj); return obj; } public static T Disp(this T obj, string format) { Console.WriteLine(string.Format(format, obj)); return obj; } public static MathObject DispLong(this MathObject obj, int indent = 0, bool comma = false) { if (obj is Or || obj is And) { Console.WriteLine(new String(' ', indent) + (obj as Function).name + "("); var i = 0; foreach (var elt in (obj as Function).args) { if (i < (obj as Function).args.Count - 1) elt.DispLong(indent + 2, comma: true); else elt.DispLong(indent + 2); i++; } Console.WriteLine(new String(' ', indent) + ")" + (comma ? "," : "")); } else Console.WriteLine(new String(' ', indent) + obj + (comma ? "," : "")); return obj; } } } ================================================ FILE: Symbolism.sln ================================================  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 16 VisualStudioVersion = 16.0.28922.388 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Symbolism", "Symbolism\Symbolism.csproj", "{9CC7DD8E-6B95-4043-AEEF-E869AF263C2E}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tests", "Tests\Tests.csproj", "{190D8FCC-CEAB-4A96-8581-063140DDC3AD}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {9CC7DD8E-6B95-4043-AEEF-E869AF263C2E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {9CC7DD8E-6B95-4043-AEEF-E869AF263C2E}.Debug|Any CPU.Build.0 = Debug|Any CPU {9CC7DD8E-6B95-4043-AEEF-E869AF263C2E}.Release|Any CPU.ActiveCfg = Release|Any CPU {9CC7DD8E-6B95-4043-AEEF-E869AF263C2E}.Release|Any CPU.Build.0 = Release|Any CPU {190D8FCC-CEAB-4A96-8581-063140DDC3AD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {190D8FCC-CEAB-4A96-8581-063140DDC3AD}.Debug|Any CPU.Build.0 = Debug|Any CPU {190D8FCC-CEAB-4A96-8581-063140DDC3AD}.Release|Any CPU.ActiveCfg = Release|Any CPU {190D8FCC-CEAB-4A96-8581-063140DDC3AD}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {9F3C1383-76DC-4249-9DD3-5D76D1E48DC7} EndGlobalSection EndGlobal ================================================ FILE: Tests/Tests.cs ================================================ /* Copyright 2013 Eduardo Cavazos Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using Xunit; using Symbolism; using Symbolism.Has; using Symbolism.Substitute; using Symbolism.SimplifyLogical; using Symbolism.LogicalExpand; using Symbolism.SimplifyEquation; using Symbolism.DegreeGpe; using Symbolism.CoefficientGpe; using Symbolism.AlgebraicExpand; using Symbolism.IsolateVariable; using Symbolism.EliminateVariable; using Symbolism.RationalExpand; using Symbolism.LeadingCoefficientGpe; using Symbolism.Trigonometric; using Symbolism.DeepSelect; using Symbolism.RationalizeExpression; using static Symbolism.Constructors; using static Symbolism.Trigonometric.Constructors; using static Symbolism.PolynomialDivision.Extensions; using static Symbolism.PolynomialGcd.Extensions; namespace SymbolismTests { public static class Extensions { // public static void AssertEqTo(this MathObject a, MathObject b) => Assert.True(a == b); public static MathObject AssertEqTo(this MathObject a, MathObject b) { Assert.True(a == b); return a; } public static MathObject MultiplyBothSidesBy(this MathObject obj, MathObject item) { //if (obj is Equation) // return (obj as Equation).a * item == (obj as Equation).b * item; if (obj is Equation) return new Equation( (obj as Equation).a * item, (obj as Equation).b * item, (obj as Equation).Operator); if (obj is And) return (obj as And).Map(elt => elt.MultiplyBothSidesBy(item)); throw new Exception(); } public static MathObject AddToBothSides(this MathObject obj, MathObject item) { if (obj is Equation) return (obj as Equation).a + item == (obj as Equation).b + item; throw new Exception(); } } public class Obj2 { public Symbol ΣFx; public Symbol ΣFy; public Symbol m; public Symbol ax; public Symbol ay; public Symbol F1, F2; public Symbol th1, th2; public Symbol F1x, F2x; public Symbol F1y, F2y; public Obj2(string name) { ΣFx = new Symbol($"{name}.ΣFx"); ΣFy = new Symbol($"{name}.ΣFy"); m = new Symbol($"{name}.m"); ax = new Symbol($"{name}.ax"); ay = new Symbol($"{name}.ay"); F1 = new Symbol($"{name}.F1"); F2 = new Symbol($"{name}.F2"); th1 = new Symbol($"{name}.th1"); th2 = new Symbol($"{name}.th2"); F1x = new Symbol($"{name}.F1x"); F2x = new Symbol($"{name}.F2x"); F1y = new Symbol($"{name}.F1y"); F2y = new Symbol($"{name}.F2y"); } public And Equations() { return new And( F1x == F1 * cos(th1), F1y == F1 * sin(th1), F2x == F2 * cos(th2), F2y == F2 * sin(th2), ΣFx == F1x + F2x, ΣFx == m * ax, ΣFy == F1y + F2y, ΣFy == m * ay ); } } public class Obj3 { public Symbol ΣFx; public Symbol ΣFy; public Symbol m; public Symbol ax; public Symbol ay; public Symbol F1, F2, F3; public Symbol th1, th2, th3; public Symbol F1x, F2x, F3x; public Symbol F1y, F2y, F3y; public Obj3(string name) { ΣFx = new Symbol($"{name}.ΣFx"); ΣFy = new Symbol($"{name}.ΣFy"); m = new Symbol($"{name}.m"); ax = new Symbol($"{name}.ax"); ay = new Symbol($"{name}.ay"); F1 = new Symbol($"{name}.F1"); F2 = new Symbol($"{name}.F2"); F3 = new Symbol($"{name}.F3"); th1 = new Symbol($"{name}.th1"); th2 = new Symbol($"{name}.th2"); th3 = new Symbol($"{name}.th3"); F1x = new Symbol($"{name}.F1x"); F2x = new Symbol($"{name}.F2x"); F3x = new Symbol($"{name}.F3x"); F1y = new Symbol($"{name}.F1y"); F2y = new Symbol($"{name}.F2y"); F3y = new Symbol($"{name}.F3y"); } public And Equations() { return new And( F1x == F1 * cos(th1), F1y == F1 * sin(th1), F2x == F2 * cos(th2), F2y == F2 * sin(th2), F3x == F3 * cos(th3), F3y == F3 * sin(th3), ΣFx == F1x + F2x + F3x, ΣFx == m * ax, ΣFy == F1y + F2y + F3y, ΣFy == m * ay ); } } public class Obj5 { public Symbol ΣFx; public Symbol ΣFy; public Symbol m; public Symbol ax; public Symbol ay; public Symbol F1, F2, F3, F4, F5; public Symbol th1, th2, th3, th4, th5; public Symbol F1x, F2x, F3x, F4x, F5x; public Symbol F1y, F2y, F3y, F4y, F5y; public Obj5(string name) { ΣFx = new Symbol($"{name}.ΣFx"); ΣFy = new Symbol($"{name}.ΣFy"); m = new Symbol($"{name}.m"); ax = new Symbol($"{name}.ax"); ay = new Symbol($"{name}.ay"); F1 = new Symbol($"{name}.F1"); F2 = new Symbol($"{name}.F2"); F3 = new Symbol($"{name}.F3"); F4 = new Symbol($"{name}.F4"); F5 = new Symbol($"{name}.F5"); th1 = new Symbol($"{name}.th1"); th2 = new Symbol($"{name}.th2"); th3 = new Symbol($"{name}.th3"); th4 = new Symbol($"{name}.th4"); th5 = new Symbol($"{name}.th5"); F1x = new Symbol($"{name}.F1x"); F2x = new Symbol($"{name}.F2x"); F3x = new Symbol($"{name}.F3x"); F4x = new Symbol($"{name}.F4x"); F5x = new Symbol($"{name}.F5x"); F1y = new Symbol($"{name}.F1y"); F2y = new Symbol($"{name}.F2y"); F3y = new Symbol($"{name}.F3y"); F4y = new Symbol($"{name}.F4y"); F5y = new Symbol($"{name}.F5y"); } public And Equations() { return new And( F1x == F1 * cos(th1), F1y == F1 * sin(th1), F2x == F2 * cos(th2), F2y == F2 * sin(th2), F3x == F3 * cos(th3), F3y == F3 * sin(th3), F4x == F4 * cos(th4), F4y == F4 * sin(th4), F5x == F5 * cos(th5), F5y == F5 * sin(th5), ΣFx == F1x + F2x + F3x + F4x + F5x, ΣFx == m * ax, ΣFy == F1y + F2y + F3y + F4y + F5y, ΣFy == m * ay ); } } public class KinematicObjectABC { public Symbol xA, yA, vxA, vyA, vA, thA; public Symbol xB, yB, vxB, vyB, vB, thB; public Symbol xC, yC, vxC, vyC, vC, thC; public Symbol tAB, tBC, tAC; public Symbol ax, ay; public KinematicObjectABC(string name) { xA = new Symbol($"{name}.xA"); yA = new Symbol($"{name}.yA"); vxA = new Symbol($"{name}.vxA"); vyA = new Symbol($"{name}.vyA"); vA = new Symbol($"{name}.vA"); thA = new Symbol($"{name}.thA"); xB = new Symbol($"{name}.xB"); yB = new Symbol($"{name}.yB"); vxB = new Symbol($"{name}.vxB"); vyB = new Symbol($"{name}.vyB"); vB = new Symbol($"{name}.vB"); thB = new Symbol($"{name}.thB"); xC = new Symbol($"{name}.xC"); yC = new Symbol($"{name}.yC"); vxC = new Symbol($"{name}.vxC"); vyC = new Symbol($"{name}.vyC"); vC = new Symbol($"{name}.vC"); thC = new Symbol($"{name}.thC"); tAB = new Symbol($"{name}.tAB"); tBC = new Symbol($"{name}.tBC"); tAC = new Symbol($"{name}.tAC"); ax = new Symbol($"{name}.ax"); ay = new Symbol($"{name}.ay"); } public And EquationsAB() => new And( vxB == vxA + ax * tAB, vyB == vyA + ay * tAB, xB == xA + vxA * tAB + ax * (tAB ^ 2) / 2, yB == yA + vyA * tAB + ay * (tAB ^ 2) / 2 ); public And EquationsBC() => new And( vxC == vxB + ax * tBC, vyC == vyB + ay * tBC, xC == xB + vxB * tBC + ax * (tBC ^ 2) / 2, yC == yB + vyB * tBC + ay * (tBC ^ 2) / 2 ); public And EquationsAC() => new And( vxC == vxA + ax * tAC, vyC == vyA + ay * tAC, xC == xA + vxA * tAC + ax * (tAC ^ 2) / 2, yC == yA + vyA * tAC + ay * (tAC ^ 2) / 2 ); public And TrigEquationsA() => new And( vxA == vA * cos(thA), vyA == vA * sin(thA) ); } public class Tests { Symbol a = new Symbol("a"); Symbol b = new Symbol("b"); Symbol c = new Symbol("c"); Symbol d = new Symbol("d"); Symbol w = new Symbol("w"); Symbol x = new Symbol("x"); Symbol y = new Symbol("y"); Symbol z = new Symbol("z"); Integer Int(int n) => new Integer(n); #region [Fact] public void Test1() => Assert.True(new DoubleFloat(1.2).Equals(new DoubleFloat(1.2))); [Fact] public void Test2() => Assert.False(new DoubleFloat(1.20000001).Equals(new DoubleFloat(1.20000002))); [Fact] public void Test3() { DoubleFloat.tolerance = 0.000000001; Assert.True(new DoubleFloat(1.2000000000001).Equals(new DoubleFloat(1.200000000002))); DoubleFloat.tolerance = null; } [Fact] public void Test4() => Assert.False(new DoubleFloat(1.2).Equals(new DoubleFloat(1.23))); #endregion #region Const [Fact] public void Test5() => Assert.True((2 * x * y).Const() == 2); [Fact] public void Test6() => Assert.True((x * y / 2).Const() == new Integer(1) / 2); [Fact] public void Test7() => Assert.True((0.1 * x * y).Const() == 0.1); [Fact] public void Test8() => Assert.True((x * y).Const() == 1); #endregion #region Simplify [Fact] public void Test9() => Assert.True(x + x == 2 * x); [Fact] public void Test10() => Assert.True(x + x + x == 3 * x); [Fact] public void Test11() => Assert.True(5 + x + 2 == 7 + x); [Fact] public void Test12() => Assert.True(3 + x + 5 + x == 8 + 2 * x); [Fact] public void Test13() => Assert.True(4 * x + 3 * x == 7 * x); [Fact] public void Test14() => Assert.True(x + y + z + x + y + z == 2 * x + 2 * y + 2 * z); [Fact] public void Test15() => Assert.True(10 - x == 10 + x * -1); [Fact] public void Test16() => Assert.True(x * y / 3 == Int(1) / 3 * x * y); [Fact] public void Test17() => Assert.True(x / y == x * (y ^ -1)); [Fact] public void Test18() => Assert.True(x / 3 == x * (Int(1) / 3)); [Fact] public void Test19() => Assert.True(6 * x * y / 3 == 2 * x * y); [Fact] public void Test20() => Assert.True((((x ^ Int(1) / 2) ^ Int(1) / 2) ^ 8) == (x ^ 2)); [Fact] public void Test21() => Assert.True(((((x * y) ^ (Int(1) / 2)) * (z ^ 2)) ^ 2) == (x * y * (z ^ 4))); [Fact] public void Test22() => Assert.True(x / x == 1); [Fact] public void Test23() => Assert.True(x / y * y / x == 1); [Fact] public void Test24() => Assert.True((x ^ 2) * (x ^ 3) == (x ^ 5)); [Fact] public void Test25() => Assert.True(x + y + x + z + 5 + z == 5 + 2 * x + y + 2 * z); [Fact] public void Test26() => Assert.True(((Int(1) / 2) * x + (Int(3) / 4) * x) == Int(5) / 4 * x); [Fact] public void Test27() => Assert.True(1.2 * x + 3 * x == 4.2 * x); [Fact] public void Test28() => Assert.True(3 * x + 1.2 * x == 4.2 * x); [Fact] public void Test29() => Assert.True(1.2 * x * 3 * y == 3.5999999999999996 * x * y); [Fact] public void Test30() => Assert.True(3 * x * 1.2 * y == 3.5999999999999996 * x * y); [Fact] public void Test31() => Assert.True(3.4 * x * 1.2 * y == 4.08 * x * y); [Fact] public void Test32() => Assert.True((a == b) == (a == b)); #endregion #region Power.Simplify [Fact] public void Test33() => Assert.True((0 ^ x) == 0); [Fact] public void Test34() => Assert.True((1 ^ x) == 1); [Fact] public void Test35() => Assert.True((x ^ 0) == 1); [Fact] public void Test36() => Assert.True((x ^ 1) == x); #endregion // Product.Simplify [Fact] public void Test37() => Assert.True(x * 0 == 0); // Difference [Fact] public void Test38() => Assert.True(-x == -1 * x); [Fact] public void Test39() => Assert.True(x - y == x + -1 * y); #region Substitute [Fact] public void Test40() => Assert.True(Int(10).Substitute(Int(10), 20) == 20); [Fact] public void Test41() => Assert.True(Int(10).Substitute(Int(15), 20) == 10); [Fact] public void Test42() => Assert.True(new DoubleFloat(1.0).Substitute(new DoubleFloat(1.0), 2.0) == 2.0); [Fact] public void Test43() => Assert.True(new DoubleFloat(1.0).Substitute(new DoubleFloat(1.5), 2.0) == 1.0); [Fact] public void Test44() => Assert.True((Int(1) / 2).Substitute(Int(1) / 2, Int(3) / 4) == Int(3) / 4); [Fact] public void Test45() => Assert.True((Int(1) / 2).Substitute(Int(1) / 3, Int(3) / 4) == Int(1) / 2); [Fact] public void Test46() => Assert.True(x.Substitute(x, y) == y); [Fact] public void Test47() => Assert.True(x.Substitute(y, y) == x); [Fact] public void Test48() => Assert.True((x ^ y).Substitute(x, 10) == (10 ^ y)); [Fact] public void Test49() => Assert.True((x ^ y).Substitute(y, 10) == (x ^ 10)); [Fact] public void Test50() => Assert.True((x ^ y).Substitute(x ^ y, 10) == 10); [Fact] public void Test51() => Assert.True((x * y * z).Substitute(x, y) == ((y ^ 2) * z)); [Fact] public void Test52() => Assert.True((x * y * z).Substitute(x * y * z, x) == x); [Fact] public void Test53() => Assert.True((x + y + z).Substitute(x, y) == ((y * 2) + z)); [Fact] public void Test54() => Assert.True((x + y + z).Substitute(x + y + z, x) == x); [Fact] public void Test55() => Assert.True(((((x * y) ^ (Int(1) / 2)) * (z ^ 2)) ^ 2).Substitute(x, 10).Substitute(y, 20).Substitute(z, 3) == 16200); #region Equation.Substitute [Fact] public void Test56() => Assert.True((x == y).Substitute(y, z) == (x == z)); [Fact] public void Test57() => Assert.True((x != y).Substitute(y, z) == (x != z)); [Fact] public void Test58() => (x == 0).Substitute(x, 0).AssertEqTo(true); [Fact] public void Test59() => (x == 0).Substitute(x, 1).AssertEqTo(false); [Fact] public void Test60() => (x != 0).Substitute(x, 0).AssertEqTo(false); [Fact] public void Test61() => (x != 0).Substitute(x, 1).AssertEqTo(true); #endregion #endregion [Fact] public void Test62() => Assert.True(sin(new DoubleFloat(3.14159 / 2)) == 0.99999999999911982); [Fact] public void Test63() => Assert.True(sin(x + y) + sin(x + y) == 2 * sin(x + y)); [Fact] public void Test64() => Assert.True(sin(x + x) == sin(2 * x)); [Fact] public void Test65() => Assert.True(sin(x + x).Substitute(x, 1) == sin(Int(2))); [Fact] public void Test66() => Assert.True(sin(x + x).Substitute(x, 1.0) == 0.90929742682568171); [Fact] public void Test67() => Assert.True(sin(2 * x).Substitute(x, y) == sin(2 * y)); // Product.RecursiveSimplify [Fact] public void Test68() => Assert.True(1 * x == x); [Fact] public void Test69() => Assert.True(x * 1 == x); [Fact] public void Test70() => Assert.True(x != y); [Fact] public void Test71() => Assert.True(x != 10); // ==(double a, MathObject b) [Fact] public void Test72() => Assert.True(1.0 == new DoubleFloat(3.0) - 2.0); [Fact] public void Test73() => Assert.True((a == b) != (a != b)); [Fact] public void Test74() => (sqrt(a * b) * (sqrt(a * b) / a) / c).AssertEqTo(b / c); void AssertToStringMatch(MathObject obj, string str) => Assert.True(obj.ToString() == str); [Fact] public void Test75() { MathObject.ToStringForm = MathObject.ToStringForms.Full; AssertToStringMatch(x + y + z, "x + y + z"); AssertToStringMatch(x + y * z, "x + y * z"); AssertToStringMatch((x + y) * z, "(x + y) * z"); Assert.True((sin(x) * cos(y)).ToString() == "cos(y) * sin(x)", "(sin(x) * cos(y)).ToString()"); AssertToStringMatch(and(x, y, z), "and(x, y, z)"); AssertToStringMatch(x ^ y, "x ^ y"); AssertToStringMatch((x * y) ^ (x + z), "(x * y) ^ (x + z)"); Assert.True((x - y).ToString() == "x + -1 * y", "(x - y).ToString()"); Assert.True((x - y - z).ToString() == "x + -1 * y + -1 * z", "(x - y - z).ToString()"); Assert.True((x / y).ToString() == "x * y ^ -1", "(x / y).ToString()"); Assert.True((x - (y - z)).ToString() == "x + -1 * (y + -1 * z)", "(x - (y - z)).ToString()"); } [Fact] public void Test76() { MathObject.ToStringForm = MathObject.ToStringForms.Standard; Assert.True((x + y).ToString() == "x + y", "(x + y).ToString()"); Assert.True((x - y).ToString() == "x - y", "(x - y).ToString()"); Assert.True((x - y - z).ToString() == "x - y - z", "(x - y - z).ToString()"); Assert.True((-x - y - z).ToString() == "-x - y - z", "(x - y - z).ToString()"); Assert.True((2 * x - 3 * y - 4 * z).ToString() == "2 * x - 3 * y - 4 * z", "(2 * x - 3 * y - 4 * z).ToString()"); Assert.True((x - (y - z)).ToString() == "x - (y - z)", "(x - (y - z)).ToString()"); Assert.True((x - y + z).ToString() == "x - y + z", "(x - y + z).ToString()"); Assert.True((-x).ToString() == "-x", "(-x).ToString()"); Assert.True((x / y).ToString() == "x / y", "(x / y).ToString()"); Assert.True((x / (y + z)).ToString() == "x / (y + z)", "(x / (y + z)).ToString()"); Assert.True(((x + y) / (x + z)).ToString() == "(x + y) / (x + z)", "((x + y) / (x + z)).ToString()"); Assert.True((-x * y).ToString() == "-x * y", "(-x * y).ToString()"); Assert.True((x * -y).ToString() == "-x * y", "(x * -y).ToString()"); Assert.True(sin(x / y).ToString() == "sin(x / y)", "sin(x / y).ToString()"); Assert.True( (x == -sqrt(2 * y * (-z * a + y * (b ^ 2) / 2 - c * y * d + c * y * z * sin(x))) / y).ToString() == "x == -sqrt(2 * y * ((b ^ 2) * y / 2 - c * d * y - a * z + c * sin(x) * y * z)) / y", "(x == -sqrt(2 * y * (-z * a + y * (b ^ 2) / 2 - c * y * d + c * y * z * sin(x))) / y).ToString()"); Assert.True((x * (y ^ z)).ToString() == "x * (y ^ z)", "(x * (y ^ z)).ToString()"); Assert.True((x + (y ^ z)).ToString() == "x + (y ^ z)", "((x + (y ^ z)).ToString()"); Assert.True(sqrt(x).ToString() == "sqrt(x)", "sqrt(x).ToString()"); Assert.True(sqrt(x).FullForm().ToString() == "x ^ 1/2", "sqrt(x).FullForm()"); Assert.True((x ^ (new Integer(1) / 3)).ToString() == "x ^ 1/3", "(x ^ (new Integer(1) / 3)).ToString()"); Assert.True(and(and(x, y), and(x, z)).SimplifyLogical().ToString() == "and(x, y, z)", "and(and(x, y), and(x, z)).SimplifyLogical().ToString()"); AssertToStringMatch(x == sqrt(2 * (y * z - cos(a) * y * z)), "x == sqrt(2 * (y * z - cos(a) * y * z))"); AssertToStringMatch( a == (-c * cos(d) - b * c * sin(d) + x * y + b * x * z) / (-y - z), "a == (-c * cos(d) - b * c * sin(d) + x * y + b * x * z) / (-y - z)"); AssertToStringMatch( x == -(sin(y) / cos(y) + sqrt((sin(y) ^ 2) / (cos(y) ^ 2))) * (z ^ 2) / a, "x == -(sin(y) / cos(y) + sqrt((sin(y) ^ 2) / (cos(y) ^ 2))) * (z ^ 2) / a"); AssertToStringMatch(x * sqrt(y), "x * sqrt(y)"); AssertToStringMatch(x / sqrt(y), "x / sqrt(y)"); AssertToStringMatch(sqrt(y) / x, "sqrt(y) / x"); AssertToStringMatch((x ^ 2) / (y ^ 3), "(x ^ 2) / (y ^ 3)"); AssertToStringMatch( x == y * sqrt(-8 * a / (y * (z ^ 2))) * (z ^ 2) / (4 * a), "x == y * sqrt(-8 * a / (y * (z ^ 2))) * (z ^ 2) / (4 * a)"); AssertToStringMatch(-(-1 + x), "-(-1 + x)"); } #region Equation.ToString [Fact] public void Test77() => Assert.True((x == y).ToString() == "x == y", "x == y"); [Fact] public void Test78() => Assert.True((x != y).ToString() == "x != y", "x != y"); #endregion #region Function.ToString [Fact] public void Test79() => Assert.True(new And().ToString() == "and()", "and()"); #endregion #region Equation.Simplify [Fact] public void Test80() => (new Integer(0) == new Integer(0)).Simplify().AssertEqTo(true); [Fact] public void Test81() => (new Integer(0) == new Integer(1)).Simplify().AssertEqTo(false); [Fact] public void Test82() => (new Integer(0) != new Integer(1)).Simplify().AssertEqTo(true); [Fact] public void Test83() => (new Integer(0) != new Integer(0)).Simplify().AssertEqTo(false); #endregion #region And [Fact] public void Test84() => and().AssertEqTo(true); [Fact] public void Test85() => and(10).AssertEqTo(10); [Fact] public void Test86() => and(true).AssertEqTo(true); [Fact] public void Test87() => and(false).AssertEqTo(false); [Fact] public void Test88() => and(10, 20, 30).AssertEqTo(and(10, 20, 30)); [Fact] public void Test89() => and(10, false, 20).AssertEqTo(false); [Fact] public void Test90() => and(10, true, 20).AssertEqTo(and(10, 20)); [Fact] public void Test91() => and(10, and(20, 30), 40).AssertEqTo(and(10, 20, 30, 40)); #endregion #region Or [Fact] public void Test92() => or(10).AssertEqTo(10); [Fact] public void Test93() => or(true).AssertEqTo(true); [Fact] public void Test94() => or(false).AssertEqTo(false); [Fact] public void Test95() => or(10, 20, false).AssertEqTo(or(10, 20)); [Fact] public void Test96() => or(false, false).AssertEqTo(false); [Fact] public void Test97() => or(10, true, 20, false).AssertEqTo(true); [Fact] public void Test98() => or(10, false, 20).AssertEqTo(or(10, 20)); [Fact] public void Test99() => or(10, or(20, 30), 40).AssertEqTo(or(10, 20, 30, 40)); #endregion #region Function.Map [Fact] public void Test100() => new And(1, 2, 3, 4, 5, 6).Map(elt => elt * 2) .AssertEqTo(and(2, 4, 6, 8, 10, 12)); [Fact] public void Test101() => new And(1, 2, 3, 4, 5, 6).Map(elt => (elt is Integer) && (elt as Integer).val % 2 == 0 ? elt : false) .AssertEqTo(false); [Fact] public void Test102() => new Or(1, 2, 3).Map(elt => elt * 2) .AssertEqTo(or(2, 4, 6)); [Fact] public void Test103() => new Or(1, 2, 3, 4, 5, 6).Map(elt => (elt is Integer) && (elt as Integer).val % 2 == 0 ? elt : false) .AssertEqTo(or(2, 4, 6)); #endregion Function.Map #region Sum [Fact] public void Test104() => Assert.True((x + y).Equals(x * y) == false, "(x + y).Equals(x * y)"); #endregion [Fact] public void Test105() { (x < y).Substitute(x, 10).Substitute(y, 20).AssertEqTo(true); (x > y).Substitute(x, 10).Substitute(y, 20).AssertEqTo(false); } Symbol Pi = new Symbol("Pi"); MathObject half = new Integer(1) / 2; #region Sin [Fact] public void Test106() => sin(0).AssertEqTo(0); [Fact] public void Test107() => sin(Pi).AssertEqTo(0); [Fact] public void Test108() => sin(-10).AssertEqTo(-sin(10)); [Fact] public void Test109() => sin(-x).AssertEqTo(-sin(x)); [Fact] public void Test110() => sin(-5 * x).AssertEqTo(-sin(5 * x)); // sin(k/n pi) for n = 1 2 3 4 6 [Fact] public void Test111() => sin(-2 * Pi).AssertEqTo(0); [Fact] public void Test112() => sin(-1 * Pi).AssertEqTo(0); [Fact] public void Test113() => sin(2 * Pi).AssertEqTo(0); [Fact] public void Test114() => sin(3 * Pi).AssertEqTo(0); [Fact] public void Test115() => sin(-7 * Pi / 2).AssertEqTo(1); [Fact] public void Test116() => sin(-5 * Pi / 2).AssertEqTo(-1); [Fact] public void Test117() => sin(-3 * Pi / 2).AssertEqTo(1); [Fact] public void Test118() => sin(-1 * Pi / 2).AssertEqTo(-1); [Fact] public void Test119() => sin(1 * Pi / 2).AssertEqTo(1); [Fact] public void Test120() => sin(3 * Pi / 2).AssertEqTo(-1); [Fact] public void Test121() => sin(5 * Pi / 2).AssertEqTo(1); [Fact] public void Test122() => sin(7 * Pi / 2).AssertEqTo(-1); [Fact] public void Test123() => sin(-4 * Pi / 3).AssertEqTo(sqrt(3) / 2); [Fact] public void Test124() => sin(-2 * Pi / 3).AssertEqTo(-sqrt(3) / 2); [Fact] public void Test125() => sin(-1 * Pi / 3).AssertEqTo(-sqrt(3) / 2); [Fact] public void Test126() => sin(1 * Pi / 3).AssertEqTo(sqrt(3) / 2); [Fact] public void Test127() => sin(2 * Pi / 3).AssertEqTo(sqrt(3) / 2); [Fact] public void Test128() => sin(4 * Pi / 3).AssertEqTo(-sqrt(3) / 2); [Fact] public void Test129() => sin(5 * Pi / 3).AssertEqTo(-sqrt(3) / 2); [Fact] public void Test130() => sin(7 * Pi / 3).AssertEqTo(sqrt(3) / 2); [Fact] public void Test131() => sin(-3 * Pi / 4).AssertEqTo(-1 / sqrt(2)); [Fact] public void Test132() => sin(-1 * Pi / 4).AssertEqTo(-1 / sqrt(2)); [Fact] public void Test133() => sin(1 * Pi / 4).AssertEqTo(1 / sqrt(2)); [Fact] public void Test134() => sin(3 * Pi / 4).AssertEqTo(1 / sqrt(2)); [Fact] public void Test135() => sin(5 * Pi / 4).AssertEqTo(-1 / sqrt(2)); [Fact] public void Test136() => sin(7 * Pi / 4).AssertEqTo(-1 / sqrt(2)); [Fact] public void Test137() => sin(9 * Pi / 4).AssertEqTo(1 / sqrt(2)); [Fact] public void Test138() => sin(11 * Pi / 4).AssertEqTo(1 / sqrt(2)); [Fact] public void Test139() => sin(-5 * Pi / 6).AssertEqTo(-half); [Fact] public void Test140() => sin(-1 * Pi / 6).AssertEqTo(-half); [Fact] public void Test141() => sin(1 * Pi / 6).AssertEqTo(half); [Fact] public void Test142() => sin(5 * Pi / 6).AssertEqTo(half); [Fact] public void Test143() => sin(7 * Pi / 6).AssertEqTo(-half); [Fact] public void Test144() => sin(11 * Pi / 6).AssertEqTo(-half); [Fact] public void Test145() => sin(13 * Pi / 6).AssertEqTo(half); [Fact] public void Test146() => sin(17 * Pi / 6).AssertEqTo(half); // sin(a/b pi) where a/b > 1/2 (i.e. not in first quadrant) [Fact] public void Test147() => sin(15 * Pi / 7).AssertEqTo(sin(1 * Pi / 7)); [Fact] public void Test148() => sin(8 * Pi / 7).AssertEqTo(-sin(1 * Pi / 7)); [Fact] public void Test149() => sin(4 * Pi / 7).AssertEqTo(sin(3 * Pi / 7)); // sin( a + b + ... + n * pi ) where abs(n) >= 2 [Fact] public void Test150() => sin(x - 3 * Pi).AssertEqTo(sin(x + Pi)); [Fact] public void Test151() => sin(x - 2 * Pi).AssertEqTo(sin(x)); [Fact] public void Test152() => sin(x + 2 * Pi).AssertEqTo(sin(x)); [Fact] public void Test153() => sin(x + 3 * Pi).AssertEqTo(sin(x + Pi)); [Fact] public void Test154() => sin(x + 7 * Pi / 2).AssertEqTo(sin(x + 3 * Pi / 2)); // sin( a + b + ... + n/2 * pi ) [Fact] public void Test155() => sin(x - 3 * Pi / 2).AssertEqTo(cos(x)); [Fact] public void Test156() => sin(x - 1 * Pi / 2).AssertEqTo(-cos(x)); [Fact] public void Test157() => sin(x + 1 * Pi / 2).AssertEqTo(cos(x)); [Fact] public void Test158() => sin(x + 3 * Pi / 2).AssertEqTo(-cos(x)); [Fact] public void Test159() => sin(Pi + x).AssertEqTo(-sin(x)); [Fact] public void Test160() => sin(Pi + x + y).AssertEqTo(-sin(x + y)); [Fact] public void Test161() => cos(Pi + x).AssertEqTo(-cos(x)); [Fact] public void Test162() => cos(Pi + x + y).AssertEqTo(-cos(x + y)); #endregion #region Cos [Fact] public void Test163() => cos(0).AssertEqTo(1); [Fact] public void Test164() => cos(Pi).AssertEqTo(-1); [Fact] public void Test165() => cos(-10).AssertEqTo(cos(10)); [Fact] public void Test166() => cos(-10 * x).AssertEqTo(cos(10 * x)); [Fact] public void Test167() => cos(3 * Pi).AssertEqTo(-1); [Fact] public void Test168() => cos(2 * Pi * 3 / 4).AssertEqTo(0); // cos( a + b + ... + n * pi ) where abs(n) >= 2 [Fact] public void Test169() => cos(x - 3 * Pi).AssertEqTo(cos(x + Pi)); [Fact] public void Test170() => cos(x + 3 * Pi).AssertEqTo(cos(x + Pi)); [Fact] public void Test171() => cos(x - 2 * Pi).AssertEqTo(cos(x)); [Fact] public void Test172() => cos(x + 2 * Pi).AssertEqTo(cos(x)); [Fact] public void Test173() => cos(x + Pi * 7 / 2).AssertEqTo(cos(x + Pi * 3 / 2)); // cos( a + b + ... + n/2 * pi ) [Fact] public void Test174() => cos(x - Pi * 3 / 2).AssertEqTo(-sin(x)); [Fact] public void Test175() => cos(x - Pi * 1 / 2).AssertEqTo(sin(x)); [Fact] public void Test176() => cos(x + Pi * 1 / 2).AssertEqTo(-sin(x)); [Fact] public void Test177() => cos(x + Pi * 3 / 2).AssertEqTo(sin(x)); #endregion #region Has [Fact] public void Test178() => Assert.True(a.Has(elt => elt == a), "a.Has(elt => elt == a)"); [Fact] public void Test179() => Assert.True(a.Has(elt => elt == b) == false, "a.Has(elt => elt == b) == false"); [Fact] public void Test180() => Assert.True((a == b).Has(elt => elt == a), "Has - 3"); [Fact] public void Test181() => Assert.True((a == b).Has(elt => elt == c) == false, "Has - 4"); [Fact] public void Test182() => Assert.True(((a + b) ^ c).Has(elt => elt == a + b), "Has - 5"); [Fact] public void Test183() => Assert.True(((a + b) ^ c).Has(elt => (elt is Power) && (elt as Power).exp == c), "Has - 6"); [Fact] public void Test184() => Assert.True((x * (a + b + c)).Has(elt => (elt is Sum) && (elt as Sum).Has(b)), "Has - 7"); [Fact] public void Test185() => Assert.True((x * (a + b + c)).Has(elt => (elt is Sum) && (elt as Sum).elts.Any(obj => obj == b)), "Has - 8"); [Fact] public void Test186() => Assert.True((x * (a + b + c)).Has(elt => (elt is Product) && (elt as Product).elts.Any(obj => obj == b)) == false, "Has - 9"); #endregion #region FreeOf [Fact] public void Test187() => Assert.True((a + b).FreeOf(b) == false, "(a + b).FreeOf(b)"); [Fact] public void Test188() => Assert.True((a + b).FreeOf(c) == true, "(a + b).FreeOf(c)"); [Fact] public void Test189() => Assert.True(((a + b) * c).FreeOf(a + b) == false, "((a + b) * c).FreeOf(a + b)"); [Fact] public void Test190() => Assert.True((sin(x) + 2 * x).FreeOf(sin(x)) == false, "(sin(x) + 2 * x).FreeOf(sin(x))"); [Fact] public void Test191() => Assert.True(((a + b + c) * d).FreeOf(a + b) == true, "((a + b + c) * d).FreeOf(a + b)"); [Fact] public void Test192() => Assert.True(((y + 2 * x - y) / x).FreeOf(x) == true, "((y + 2 * x - y) / x).FreeOf(x)"); [Fact] public void Test193() => Assert.True(((x * y) ^ 2).FreeOf(x * y) == true, "((x * y) ^ 2).FreeOf(x * y)"); #endregion #region Numerator [Fact] public void Test194() => Assert.True((x ^ -1).Numerator() == 1); [Fact] public void Test195() => Assert.True((x ^ -half).Numerator() == 1); #endregion #region Denominator [Fact] public void Test196() => ((new Integer(2) / 3) * ((x * (x + 1)) / (x + 2)) * (y ^ z)).Denominator().AssertEqTo(3 * (x + 2)); #endregion #region LogicalExpand [Fact] public void Test197() => and(or(a, b), c).LogicalExpand() .AssertEqTo( or( and(a, c), and(b, c))); [Fact] public void Test198() => and(a, or(b, c)) .LogicalExpand() .AssertEqTo(or(and(a, b), and(a, c))); [Fact] public void Test199() => and(a, or(b, c), d) .LogicalExpand() .AssertEqTo( or( and(a, b, d), and(a, c, d))); [Fact] public void Test200() => and(or(a == b, b == c), x == y) .LogicalExpand() .AssertEqTo( or( and(a == b, x == y), and(b == c, x == y))); [Fact] public void Test201() => and( or(a == b, b == c), or(c == d, d == a), x == y) .LogicalExpand() .AssertEqTo( or( and(a == b, c == d, x == y), and(a == b, d == a, x == y), and(b == c, c == d, x == y), and(b == c, d == a, x == y))); #endregion #region SimplifyEquation [Fact] public void Test202() => (2 * x == 0) .SimplifyEquation() .AssertEqTo(x == 0); [Fact] public void Test203() => (2 * x != 0) .SimplifyEquation() .AssertEqTo(x != 0); [Fact] public void Test204() => ((x ^ 2) == 0) .SimplifyEquation() .AssertEqTo(x == 0); #endregion #region SimplifyLogical [Fact] public void Test205() => and(a, b, c, a) .SimplifyLogical() .AssertEqTo(and(a, b, c)); #endregion SimplifyLogical #region DegreeGpe [Fact] public void Test206() => Assert.True( ((3 * w * x ^ 2) * (y ^ 3) * (z ^ 4)).DegreeGpe(new List() { x, z }) == 6, "((3 * w * x ^ 2) * (y ^ 3) * (z ^ 4)).DegreeGpe(new List() { x, z })"); [Fact] public void Test207() => Assert.True( ((a * x ^ 2) + b * x + c).DegreeGpe(new List() { x }) == 2, "((a * x ^ 2) + b * x + c).DegreeGpe(new List() { x })"); [Fact] public void Test208() => Assert.True( (a * (sin(x) ^ 2) + b * sin(x) + c).DegreeGpe(new List() { sin(x) }) == 2, "(a * (sin(x) ^ 2) + b * sin(x) + c).DegreeGpe(new List() { sin(x) })"); [Fact] public void Test209() => Assert.True( (2 * (x ^ 2) * y * (z ^ 3) + w * x * (z ^ 6)).DegreeGpe(new List() { x, z }) == 7, "(2 * (x ^ 2) * y * (z ^ 3) + w * x * (z ^ 6)).DegreeGpe(new List() { x, z })"); #endregion #region CoefficientGpe [Fact] public void Test210() => Assert.True((a * (x ^ 2) + b * x + c).CoefficientGpe(x, 2) == a); [Fact] public void Test211() => Assert.True((3 * x * (y ^ 2) + 5 * (x ^ 2) * y + 7 * x + 9).CoefficientGpe(x, 1) == 3 * (y ^ 2) + 7); [Fact] public void Test212() => Assert.True((3 * x * (y ^ 2) + 5 * (x ^ 2) * y + 7 * x + 9).CoefficientGpe(x, 3) == 0); [Fact] public void Test213() => Assert.True( (3 * sin(x) * (x ^ 2) + 2 * x + 4).CoefficientGpe(x, 2) == null, "(3 * sin(x) * (x ^ 2) + 2 * x + 4).CoefficientGpe(x, 2) == null"); #endregion #region AlgebraicExpand [Fact] public void Test214() => Assert.True( ((x + 2) * (x + 3) * (x + 4)).AlgebraicExpand() == 24 + 26 * x + 9 * (x ^ 2) + (x ^ 3)); [Fact] public void Test215() => Assert.True( ((x + y + z) ^ 3).AlgebraicExpand() == (x ^ 3) + (y ^ 3) + (z ^ 3) + 3 * (x ^ 2) * y + 3 * (y ^ 2) * x + 3 * (x ^ 2) * z + 3 * (y ^ 2) * z + 3 * (z ^ 2) * x + 3 * (z ^ 2) * y + 6 * x * y * z); [Fact] public void Test216() => Assert.True( (((x + 1) ^ 2) + ((y + 1) ^ 2)).AlgebraicExpand() == 2 + 2 * x + (x ^ 2) + 2 * y + (y ^ 2)); [Fact] public void Test217() => Assert.True( ((((x + 2) ^ 2) + 3) ^ 2).AlgebraicExpand() == 49 + 56 * x + 30 * (x ^ 2) + 8 * (x ^ 3) + (x ^ 4)); [Fact] public void Test218() => Assert.True( sin(x * (y + z)).AlgebraicExpand() == sin(x * y + x * z)); [Fact] public void Test219() => Assert.True( (a * (b + c) == x * (y + z)).AlgebraicExpand() == (a * b + a * c == x * y + x * z)); [Fact] public void Test220() => (5 * x * (500 / (x ^ 2) * (sqrt(3.0) / 4) + 1) + 2 * (x ^ 2) + (sqrt(3.0) / 2) * (x ^ 2)) .AlgebraicExpand() .AssertEqTo(1082.5317547305483 / x + 5 * x + 2.8660254037844384 * (x ^ 2)); #endregion #region IsolateVariable [Fact] public void Test221() => (x + y + z == 0).IsolateVariable(a).AssertEqTo(x + y + z == 0); // (x * a + x * b == 0).IsolateVariable(x).Disp(); [Fact] public void Test222() => (x * (a + b) - x * a - x * b + x == c) .IsolateVariable(x) .AssertEqTo(x == c); [Fact] public void Test223() => and(x == y, a == b) .IsolateVariable(b) .AssertEqTo(and(x == y, b == a)); [Fact] public void Test224() => or(and(y == x, z == x), and(b == x, c == x)) .IsolateVariable(x) .AssertEqTo(or(and(x == y, x == z), and(x == b, x == c))); [Fact] public void Test225() => Assert.True((0 == x - y).IsolateVariableEq(x).Equals(x == y), "(0 == x - y).IsolateVariable(x).Equals(x == y)"); [Fact] public void Test226() => (a * (x ^ 2) + b * x + c == 0) .IsolateVariable(x) .AssertEqTo( or( and( x == (-b + sqrt((b ^ 2) + -4 * a * c)) / (2 * a), a != 0 ), and( x == (-b - sqrt((b ^ 2) + -4 * a * c)) / (2 * a), a != 0 ), and(x == -c / b, a == 0, b != 0), and(a == 0, b == 0, c == 0) ) ); [Fact] public void Test227() => (a * (x ^ 2) + c == 0) .IsolateVariable(x) .AssertEqTo( or( and( x == sqrt(-4 * a * c) / (2 * a), a != 0 ), and( x == -sqrt(-4 * a * c) / (2 * a), a != 0 ), and(a == 0, c == 0) ) ); // a x^2 + b x + c == 0 // a x^2 + c == - b x // (a x^2 + c) / x == - b [Fact] public void Test228() => ((a * (x ^ 2) + c) / x == -b) .IsolateVariable(x) .AssertEqTo( or( and( x == (-b + sqrt((b ^ 2) + -4 * a * c)) / (2 * a), a != 0 ), and( x == (-b - sqrt((b ^ 2) + -4 * a * c)) / (2 * a), a != 0 ), and(x == -c / b, a == 0, b != 0), and(a == 0, b == 0, c == 0) )); [Fact] public void Test229() => (sqrt(x + y) == z).IsolateVariable(x).AssertEqTo(x == (z ^ 2) - y); [Fact] public void Test230() => (a * b + a == c) .IsolateVariable(a) .AssertEqTo(a == c / (b + 1)); [Fact] public void Test231() => (a * b + a * c == d) .IsolateVariable(a) .AssertEqTo(a == d / (b + c)); [Fact] public void Test232() => (1 / sqrt(x) == y) .IsolateVariable(x) .AssertEqTo(x == (y ^ -2)); [Fact] public void Test233() => (y == sqrt(x) / x) .IsolateVariable(x) .AssertEqTo(x == (y ^ -2)); [Fact] public void Test234() => (-sqrt(x) + z * x == y) .IsolateVariable(x) .AssertEqTo(-sqrt(x) + z * x == y); [Fact] public void Test235() => (sqrt(a + x) - z * x == -y) .IsolateVariable(x) .AssertEqTo(sqrt(a + x) - z * x == -y); [Fact] public void Test236() => (sqrt(2 + x) * sqrt(3 + x) == y) .IsolateVariable(x) .AssertEqTo(sqrt(2 + x) * sqrt(3 + x) == y); [Fact] public void Test237() => ((x + 1) / (x + 2) == 3) .IsolateVariable(x) .AssertEqTo(x == -new Integer(5) / 2); [Fact] public void Test238() => ((1 + 2 * x) / (3 * x - 4) == 5) .IsolateVariable(x) .AssertEqTo(x == new Integer(21) / 13); #endregion #region EliminateVariable [Fact] public void Test239() => and((x ^ 3) == (y ^ 5), z == x) .EliminateVariable(x) .AssertEqTo((z ^ 3) == (y ^ 5)); [Fact] public void Test241() => and((x ^ 3) == (y ^ 5), z == (x ^ 7)) .EliminateVariable(x) .AssertEqTo(and((x ^ 3) == (y ^ 5), z == (x ^ 7))); #endregion [Fact] public void Test242() => and(x + y == z, x / y == 0, x != 0).CheckVariable(x).AssertEqTo(false); #region RationalExpand // EA: Example 6.62 [Fact] public void Test243() => Assert.True( (((sqrt(1 / (((x + y) ^ 2) + 1)) + 1) * (sqrt(1 / (((x + y) ^ 2) + 1)) - 1)) / (x + 1)) .RationalExpand() == (-(x ^ 2) - 2 * x * y - (y ^ 2)) / (1 + x + (x ^ 2) + (x ^ 3) + 2 * x * y + 2 * (x ^ 2) * y + (y ^ 2) + x * (y ^ 2)) ); // EA: page 269 [Fact] public void Test244() => Assert.True( (1 / (1 / a + c / (a * b)) + (a * b * c + a * (c ^ 2)) / ((b + c) ^ 2) - a).RationalExpand() == 0); #endregion #region LeadingCoefficientGpe [Fact] public void Test245() => Assert.True( (3 * x * (y ^ 2) + 5 * (x ^ 2) * y + 7 * (x ^ 2) * (y ^ 3) + 9).LeadingCoefficientGpe(x) == 5 * y + 7 * (y ^ 3) ); #endregion #region PolynomialDivision // MM: Example 4.4 [Fact] public void Test246() { var result = PolynomialDivision(5 * (x ^ 2) + 4 * x + 1, 2 * x + 3, x); Assert.True(result.quotient == 5 * x / 2 - new Integer(7) / 4); Assert.True(result.remainder == new Integer(25) / 4); } // MM: Example 4.10 [Fact] public void Test247() { var result = PolynomialDivision((x ^ 2) - 4, x + 2, x); Assert.True(result.quotient == x - 2); Assert.True(result.remainder == 0); } [Fact] public void Test248() { var result = PolynomialDivision((x ^ 2) + 5 * x + 6, x + 2, x); Assert.True(result.quotient == x + 3); Assert.True(result.remainder == 0); } // MM: Example 4.43 [Fact] public void Test249() { var result = PolynomialDivision(3 * (x ^ 3) + (x ^ 2) - 4, (x ^ 2) - 4 * x + 2, x); Assert.True(result.remainder == 46 * x - 30); } // MM: Example 4.45 [Fact] public void Test250() { var i = new Symbol("i"); var result = PolynomialDivision(2 + 3 * i + 4 * (i ^ 2) + 5 * (i ^ 3) + 6 * (i ^ 4), (i ^ 2) + 1, i); Assert.True(result.remainder == 4 - 2 * i); } #endregion #region PolynomialGcd // MM: Example 4.20 [Fact] public void Test251() => Assert.True( PolynomialGcd( 2 * (x ^ 3) + 12 * (x ^ 2) + 22 * x + 12, 2 * (x ^ 3) + 18 * (x ^ 2) + 52 * x + 48, x) == (x ^ 2) + 5 * x + 6 ); // MM: Example 4.24 [Fact] public void Test252() => Assert.True( PolynomialGcd( (x ^ 7) - 4 * (x ^ 5) - (x ^ 2) + 4, (x ^ 5) - 4 * (x ^ 3) - (x ^ 2) + 4, x) == (x ^ 3) - (x ^ 2) - 4 * x + 4 ); #endregion [Fact] public void Test253_a() { var x = new Symbol("x"); var y = new Symbol("y"); var z = new Symbol("z"); var eqs = and( (x ^ 2) - 4 == 0, y + x == 0, x + z == 10 ); var half = new Integer(1) / 2; ((x ^ 2) - 4 == 0) .IsolateVariableEq(x) .AssertEqTo(or(x == half * sqrt(16), x == -half * sqrt(16))); } [Fact] public void Test253_b() { var x = new Symbol("x"); var y = new Symbol("y"); var z = new Symbol("z"); var eqs = and( (x ^ 2) - 4 == 0, y + x == 0, x + z == 10 ); var half = new Integer(1) / 2; eqs.EliminateVariable(x) .AssertEqTo( or( and( half * sqrt(16) + y == 0, half * sqrt(16) + z == 10 ), and( -half * sqrt(16) + y == 0, -half * sqrt(16) + z == 10 ) ) ); } [Fact] public void Test253() { var x = new Symbol("x"); var y = new Symbol("y"); var z = new Symbol("z"); var eqs = and( (x ^ 2) - 4 == 0, y + x == 0, x + z == 10 ); var half = new Integer(1) / 2; ((x ^ 2) - 4 == 0) .IsolateVariableEq(x) .AssertEqTo(or(x == half * sqrt(16), x == -half * sqrt(16))); eqs.EliminateVariable(x) .AssertEqTo( or( and( half * sqrt(16) + y == 0, half * sqrt(16) + z == 10 ), and( -half * sqrt(16) + y == 0, -half * sqrt(16) + z == 10 ) ) ); } And Kinematic(Symbol s, Symbol u, Symbol v, Symbol a, Symbol t) => new And( v == u + a * t, s == (u + v) * t / 2 ); [Fact] public void PSE_Example_2_6() { var sAC = new Symbol("sAC"); var sAB = new Symbol("sAB"); var vA = new Symbol("vA"); var vB = new Symbol("vB"); var vC = new Symbol("vC"); var a = new Symbol("a"); var tAC = new Symbol("tAC"); var tAB = new Symbol("tAB"); var eqs = and( tAB == tAC / 2, Kinematic(sAC, vA, vC, a, tAC), Kinematic(sAB, vA, vB, a, tAB) ); var vals = new List() { vA == 10, vC == 30, tAC == 10 }; eqs .EliminateVariables(tAB, sAC, vB, sAB) .IsolateVariable(a) .AssertEqTo(a == (vC - vA) / tAC) .SubstituteEqLs(vals) .AssertEqTo(a == 2); eqs .EliminateVariables(vB, a, tAB, sAC) .AssertEqTo(sAB == tAC / 4 * (2 * vA + (vC - vA) / 2)) .SubstituteEqLs(vals) .AssertEqTo(sAB == 75); } [Fact] public void PSE_Example_2_7() { // s = // u = 63 // v = 0 // a = // t = 2 var s = new Symbol("s"); var u = new Symbol("u"); var v = new Symbol("v"); var a = new Symbol("a"); var t = new Symbol("t"); var eqs = Kinematic(s, u, v, a, t); var vals = new List() { u == 63, v == 0, t == 2.0 }; eqs .EliminateVariable(s) .AssertEqTo(v == a * t + u) .IsolateVariable(a) .AssertEqTo(a == (v - u) / t) .SubstituteEqLs(vals) .AssertEqTo(a == -31.5); eqs .EliminateVariable(a) .SubstituteEqLs(vals) .AssertEqTo(s == 63.0); } [Fact] public void PSE_Example_2_8() { // car // // s1 = // u1 = 45 // v1 = 45 // a1 = 0 // t1 = // officer // // s2 = // u2 = 0 // v2 = // a2 = 3 // t2 var s1 = new Symbol("s1"); var u1 = new Symbol("u1"); var v1 = new Symbol("v1"); var a1 = new Symbol("a1"); var t1 = new Symbol("t1"); var s2 = new Symbol("s2"); var u2 = new Symbol("u2"); var v2 = new Symbol("v2"); var a2 = new Symbol("a2"); var t2 = new Symbol("t2"); var eqs = and( u1 == v1, s1 == s2, t2 == t1 - 1, Kinematic(s1, u1, v1, a1, t1), Kinematic(s2, u2, v2, a2, t2)); var vals = new List() { v1 == 45.0, u2 == 0, a2 == 3 }; eqs .EliminateVariables(s2, t1, a1, s1, v2, u1) .IsolateVariable(t2) .SubstituteEqLs(vals) .AssertEqTo(or(t2 == -0.96871942267131317, t2 == 30.968719422671313)); } And Kinematic(Symbol sA, Symbol sB, Symbol vA, Symbol vB, Symbol a, Symbol tA, Symbol tB) => new And( vB == vA + a * (tB - tA), sB - sA == (vA + vB) * (tB - tA) / 2); [Fact] public void PSE_Example_2_12() { var yA = new Symbol("yA"); var yB = new Symbol("yB"); var yC = new Symbol("yC"); var yD = new Symbol("yD"); var tA = new Symbol("tA"); var tB = new Symbol("tB"); var tC = new Symbol("tC"); var tD = new Symbol("tD"); var vA = new Symbol("vA"); var vB = new Symbol("vB"); var vC = new Symbol("vC"); var vD = new Symbol("vD"); var a = new Symbol("a"); var eqs = and( Kinematic(yA, yB, vA, vB, a, tA, tB), Kinematic(yB, yC, vB, vC, a, tB, tC), Kinematic(yC, yD, vC, vD, a, tC, tD)); var vals = new List() { yA == 50, yC == 50, vA == 20, vB == 0, a == -9.8, tA == 0, tD == 5 }; // velocity and position at t = 5.00 s DoubleFloat.tolerance = 0.000000001; eqs .EliminateVariables(tB, tC, vC, yB, yD) .SubstituteEqLs(vals) .AssertEqTo(or(vD == -29.000000000000004, vD == -29.000000000000007)); eqs .EliminateVariables(tB, tC, vC, yB, vD) .IsolateVariable(yD) .SubstituteEqLs(vals) .AssertEqTo(or(yD == 27.499999999, yD == 27.499999999)); DoubleFloat.tolerance = null; } [Fact] public void PSE_Example_4_3() { // A long-jumper leaves the ground at an angle of 20.0° above // the horizontal and at a speed of 11.0 m/s. // (a) How far does he jump in the horizontal direction? // (Assume his motion is equivalent to that of a particle.) // (b) What is the maximum height reached? var xA = new Symbol("xA"); var xB = new Symbol("xB"); var xC = new Symbol("xC"); var yA = new Symbol("yA"); var yB = new Symbol("yB"); var yC = new Symbol("yC"); var vxA = new Symbol("vxA"); var vxB = new Symbol("vxB"); var vxC = new Symbol("vxC"); var vyA = new Symbol("vyA"); var vyB = new Symbol("vyB"); var vyC = new Symbol("vyC"); var tAB = new Symbol("tAB"); var tAC = new Symbol("tAC"); var ax = new Symbol("ax"); var ay = new Symbol("ay"); var vA = new Symbol("vA"); var thA = new Symbol("thA"); var Pi = new Symbol("Pi"); var eqs = and( vxA == vA * cos(thA), vyA == vA * sin(thA), tAC == 2 * tAB, vxB == vxA + ax * tAB, vyB == vyA + ay * tAB, xB == xA + vxA * tAB + ax * (tAB ^ 2) / 2, yB == yA + vyA * tAB + ay * (tAB ^ 2) / 2, vxC == vxA + ax * tAB, vyC == vyA + ay * tAB, xC == xA + vxA * tAC + ax * (tAC ^ 2) / 2, yC == yA + vyA * tAC + ay * (tAC ^ 2) / 2 ); var zeros = new List() { xA == 0, yA == 0, ax == 0, vyB == 0 }; var vals = new List() { thA == (20).ToRadians(), vA == 11.0, ay == -9.8, Pi == Math.PI }; eqs .EliminateVariables(xB, yC, vxB, vxC, vyC, yB, tAC, vxA, vyA, tAB) .SubstituteEqLs(zeros) .AssertEqTo(xC == -2 * cos(thA) * sin(thA) * (vA ^ 2) / ay) .SubstituteEqLs(vals) .AssertEqTo(xC == 7.9364592624562507); eqs .EliminateVariables(xB, yC, vxB, vxC, vyC, xC, vxA, tAC, vyA, tAB) .SubstituteEqLs(zeros) .AssertEqTo(yB == -(sin(thA) ^ 2) * (vA ^ 2) / (2 * ay)) .SubstituteEqLs(vals) .AssertEqTo(yB == 0.72215873425009314); } [Fact] public void PSE_Example_4_3_KinematicObjectABC() { // A long-jumper leaves the ground at an angle of 20.0° above // the horizontal and at a speed of 11.0 m/s. // (a) How far does he jump in the horizontal direction? // (Assume his motion is equivalent to that of a particle.) // (b) What is the maximum height reached? var obj = new KinematicObjectABC("obj"); var yB = new Symbol("yB"); var xC = new Symbol("xC"); var ay = new Symbol("ay"); var thA = new Symbol("thA"); var vA = new Symbol("vA"); var Pi = new Symbol("Pi"); var eqs = and( obj.TrigEquationsA(), obj.tAC == 2 * obj.tAB, obj.EquationsAB(), obj.EquationsAC() ); var vals = new List() { obj.xA == 0, obj.yA == 0, obj.vA == vA, obj.thA == thA, obj.yB == yB, obj.vyB == 0, obj.xC == xC, obj.ax == 0, obj.ay == ay }; var numerical_vals = new List() { thA == (20).ToRadians(), vA == 11, ay == -9.8, Pi == Math.PI }; // xC eqs .SubstituteEqLs(vals) .EliminateVariables( obj.vxA, obj.vyA, obj.vyC, obj.vxC, obj.vxB, obj.xB, yB, obj.yC, obj.tAC, obj.tAB ) .AssertEqTo(xC == -2 * cos(thA) * sin(thA) * (vA ^ 2) / ay) .SubstituteEqLs(numerical_vals) .AssertEqTo(xC == 7.9364592624562507); // yB eqs .SubstituteEqLs(vals) .EliminateVariables( obj.tAB, obj.tAC, obj.vxA, obj.vxB, obj.vxC, obj.vyC, obj.vyA, obj.xB, xC, obj.yC ) .AssertEqTo(yB == -(sin(thA) ^ 2) * (vA ^ 2) / (2 * ay)) .SubstituteEqLs(numerical_vals) .AssertEqTo(yB == 0.72215873425009314); } [Fact] public void PSE_5E_Example_4_5() { var xA = new Symbol("xA"); var xB = new Symbol("xB"); var xC = new Symbol("xC"); var yA = new Symbol("yA"); var yB = new Symbol("yB"); var yC = new Symbol("yC"); var vxA = new Symbol("vxA"); var vxB = new Symbol("vxB"); var vxC = new Symbol("vxC"); var vyA = new Symbol("vyA"); var vyB = new Symbol("vyB"); var vyC = new Symbol("vyC"); var tAB = new Symbol("tAB"); var tAC = new Symbol("tAC"); var ax = new Symbol("ax"); var ay = new Symbol("ay"); var vA = new Symbol("vA"); var thA = new Symbol("thA"); var vC = new Symbol("vC"); var Pi = new Symbol("Pi"); var eqs = and( vxA == vA * cos(thA), vyA == vA * sin(thA), // tAC == 2 * tAB, // vxB == vxA + ax * tAB, // vyB == vyA + ay * tAB, // xB == xA + vxA * tAB + ax * (tAB ^ 2) / 2, // yB == yA + vyA * tAB + ay * (tAB ^ 2) / 2, vxC == vxA + ax * tAC, vyC == vyA + ay * tAC, // xC == xA + vxA * tAC + ax * (tAC ^ 2) / 2, yC == yA + vyA * tAC + ay * (tAC ^ 2) / 2, vC == sqrt((vxC ^ 2) + (vyC ^ 2)), ay != 0 ); var zeros = new List() { ax == 0, yC == 0 }; var vals = new List() { yA == 45, vA == 20, thA == (30).ToRadians(), ay == -9.8, Pi == Math.PI }; DoubleFloat.tolerance = 0.00001; eqs .EliminateVariables(vC, vxA, vxC, vyC, vyA) .IsolateVariable(tAC) .LogicalExpand().SimplifyEquation().SimplifyLogical() .CheckVariable(ay) .AssertEqTo( or( and( tAC == -(sin(thA) * vA + sqrt((sin(thA) ^ 2) * (vA ^ 2) + 2 * ay * (yC - yA))) / ay, ay != 0), and( tAC == -(sin(thA) * vA - sqrt((sin(thA) ^ 2) * (vA ^ 2) + 2 * ay * (yC - yA))) / ay, ay != 0))) .SubstituteEqLs(zeros) .SubstituteEqLs(vals) .AssertEqTo(or(tAC == 4.2180489012229376, tAC == -2.1772325746923267)); eqs .SubstituteEqLs(zeros) .EliminateVariables(vxC, vxA, vyA, vyC, tAC) .SimplifyEquation().SimplifyLogical() .CheckVariable(ay) .AssertEqTo( or( and( ay != 0, vC == sqrt((cos(thA) ^ 2) * (vA ^ 2) + ((sin(thA) * vA - (sin(thA) * vA + sqrt((sin(thA) ^ 2) * (vA ^ 2) + -2 * ay * yA))) ^ 2))), and( ay != 0, vC == sqrt((cos(thA) ^ 2) * (vA ^ 2) + ((sin(thA) * vA - (sin(thA) * vA - sqrt((sin(thA) ^ 2) * (vA ^ 2) + -2 * ay * yA))) ^ 2))))) .SubstituteEqLs(vals) .AssertEqTo(or(vC == 35.805027579936315, vC == 35.805027579936322)); DoubleFloat.tolerance = null; } [Fact] public void PSE_5E_Example_4_6() { // An Alaskan rescue plane drops a package of emergency rations to // a stranded party of explorers, as shown in Figure 4.13. // If the plane is traveling horizontally at 40.0 m/s and is // 100 m above the ground, where does the package strike the // ground relative to the point at which it was released? // What are the horizontal and vertical components // of the velocity of the package just before it hits the ground? var xA = new Symbol("xA"); var xB = new Symbol("xB"); var yA = new Symbol("yA"); var yB = new Symbol("yB"); var vxA = new Symbol("vxA"); var vxB = new Symbol("vxB"); var vyA = new Symbol("vyA"); var vyB = new Symbol("vyB"); var tAB = new Symbol("tAB"); var ax = new Symbol("ax"); var ay = new Symbol("ay"); var Pi = new Symbol("Pi"); var eqs = and( vxB == vxA + ax * tAB, vyB == vyA + ay * tAB, xB == xA + vxA * tAB + ax * (tAB ^ 2) / 2, yB == yA + vyA * tAB + ay * (tAB ^ 2) / 2, vxA != 0, ay != 0 ); var vals = new List() { xA == 0, yA == 100, vxA == 40, vyA == 0, yB == 0, ax == 0, ay == -9.8, Pi == Math.PI }; var zeros = vals.Where(eq => eq.b == 0).ToList(); DoubleFloat.tolerance = 0.00001; eqs .EliminateVariables(vxB, vyB, tAB) .IsolateVariable(xB) .LogicalExpand().SimplifyEquation() .CheckVariable(ay) .CheckVariable(vxA).SimplifyLogical() .SubstituteEq(ax == 0) .AssertEqTo( or( and( vxA != 0, xB == -(vxA ^ 2) * (-(-vyA / vxA + ay / (vxA ^ 2) * xA) + sqrt(((-vyA / vxA + ay * xA / (vxA ^ 2)) ^ 2) + 2 * ay * (vyA * xA / vxA - ay / 2 / (vxA ^ 2) * (xA ^ 2) - yA + yB) / (vxA ^ 2))) / ay, ay / (vxA ^ 2) != 0, ay != 0), and( vxA != 0, xB == -(vxA ^ 2) * (-(-vyA / vxA + ay / (vxA ^ 2) * xA) - sqrt(((-vyA / vxA + ay * xA / (vxA ^ 2)) ^ 2) + 2 * ay * (vyA * xA / vxA - ay / 2 / (vxA ^ 2) * (xA ^ 2) - yA + yB) / (vxA ^ 2))) / ay, ay / (vxA ^ 2) != 0, ay != 0))) .SubstituteEqLs(zeros) .AssertEqTo( or( and( vxA != 0, xB == -1 / ay * (vxA ^ 2) * sqrt(-2 * ay * (vxA ^ -2) * yA), ay / (vxA ^ 2) != 0, ay != 0), and( vxA != 0, xB == 1 / ay * (vxA ^ 2) * sqrt(-2 * ay * (vxA ^ -2) * yA), ay / (vxA ^ 2) != 0, ay != 0))) .SubstituteEqLs(vals) .AssertEqTo(or(xB == 180.70158058105022, xB == -180.70158058105022)); eqs .EliminateVariables(vxB, xB, tAB) .IsolateVariable(vyB) .LogicalExpand().SimplifyEquation() .CheckVariable(ay) .AssertEqTo( or( and( vyB == -1 * ay * sqrt(2 * (ay ^ -1) * ((ay ^ -1) / 2 * (vyA ^ 2) + -1 * yA + yB)), vxA != 0, ay != 0), and( vyB == ay * sqrt(2 * (ay ^ -1) * ((ay ^ -1) / 2 * (vyA ^ 2) + -1 * yA + yB)), vxA != 0, ay != 0))) .SubstituteEqLs(zeros) .AssertEqTo( or( and( vyB == -ay * sqrt(-2 / ay * yA), vxA != 0, ay != 0), and( vyB == ay * sqrt(-2 / ay * yA), vxA != 0, ay != 0))) .SubstituteEqLs(vals) .AssertEqTo(or(vyB == 44.271887242357309, vyB == -44.271887242357309)); DoubleFloat.tolerance = null; } [Fact] public void PSE_5E_Example_4_7() { var xA = new Symbol("xA"); var yA = new Symbol("yA"); var xB = new Symbol("xB"); var yB = new Symbol("yB"); var vxA = new Symbol("vxA"); var vyA = new Symbol("vyA"); var vxB = new Symbol("vxB"); var vyB = new Symbol("vyB"); var tAB = new Symbol("tAB"); var ax = new Symbol("ax"); var ay = new Symbol("ay"); var th = new Symbol("th"); var d = new Symbol("d"); var Pi = new Symbol("Pi"); var eqs = and( cos(th) == (xB - xA) / d, sin(th) == (yA - yB) / d, vxB == vxA + ax * tAB, vyB == vyA + ay * tAB, xB == xA + vxA * tAB + ax * (tAB ^ 2) / 2, yB == yA + vyA * tAB + ay * (tAB ^ 2) / 2, yB != 0, ay != 0 ); var vals = new List() { xA == 0, yA == 0, vxA == 25, vyA == 0, ax == 0, ay == -9.8, th == (35).ToRadians(), Pi == Math.PI }; var zeros = vals.Where(eq => eq.b == 0).ToList(); DoubleFloat.tolerance = 0.00001; eqs .SubstituteEqLs(zeros) .EliminateVariables(vxB, vyB, d, yB, tAB) .IsolateVariable(xB) .LogicalExpand() .CheckVariable(ay) .SimplifyEquation() .AssertEqTo( or( and( xB == -(sin(th) / cos(th) + sqrt((cos(th) ^ -2) * (sin(th) ^ 2))) * (vxA ^ 2) / ay, ay / (vxA ^ 2) != 0, sin(th) / cos(th) * xB != 0, ay != 0), and( xB == -(sin(th) / cos(th) - sqrt((cos(th) ^ -2) * (sin(th) ^ 2))) * (vxA ^ 2) / ay, ay / (vxA ^ 2) != 0, sin(th) / cos(th) * xB != 0, ay != 0))) .SubstituteEqLs(vals) .SimplifyEquation() .AssertEqTo( or( and( xB == 89.312185996136435, xB != 0), and( xB == 7.0805039835788038E-15, xB != 0))); eqs .SubstituteEqLs(zeros) .EliminateVariables(vxB, vyB, d, xB, tAB) .IsolateVariable(yB) .LogicalExpand() .CheckVariable(yB) .AssertEqTo( and( yB == 2 * (sin(th) ^ 2) * (vxA ^ 2) / ay / (cos(th) ^ 2), -ay * (cos(th) ^ 2) / (sin(th) ^ 2) / (vxA ^ 2) / 2 != 0, yB != 0, ay != 0)) .SubstituteEqLs(vals) .AssertEqTo( and( yB == -62.537065888482395, yB != 0)); eqs .SubstituteEqLs(zeros) .EliminateVariables(vxB, vyB, d, xB, yB) .IsolateVariable(tAB) .LogicalExpand().CheckVariable(ay).SimplifyEquation().SimplifyLogical() .AssertEqTo( or( and( tAB == -(sin(th) * vxA / cos(th) + sqrt((sin(th) ^ 2) * (vxA ^ 2) / (cos(th) ^ 2))) / ay, ay != 0, sin(th) * tAB * vxA / cos(th) != 0), and( tAB == -(sin(th) * vxA / cos(th) - sqrt((sin(th) ^ 2) * (vxA ^ 2) / (cos(th) ^ 2))) / ay, ay != 0, sin(th) * tAB * vxA / cos(th) != 0))) .SubstituteEqLs(vals) .CheckVariable(tAB).SimplifyEquation() .AssertEqTo( and( tAB == 3.5724874398454571, tAB != 0)); eqs .SubstituteEqLs(zeros) .EliminateVariables(vxB, d, tAB, xB, yB) .IsolateVariable(vyB) .LogicalExpand() .CheckVariable(ay) .SimplifyEquation() .CheckVariable(ay) .AssertEqTo( or( and( vyB == -ay * (sin(th) * vxA / (ay * cos(th)) + sqrt((sin(th) ^ 2) * (vxA ^ 2) / ((ay ^ 2) * (cos(th) ^ 2)))), sin(th) * vxA * vyB / (ay * cos(th)) != 0, ay != 0), and( vyB == -ay * (sin(th) * vxA / (ay * cos(th)) - sqrt((sin(th) ^ 2) * (vxA ^ 2) / ((ay ^ 2) * (cos(th) ^ 2)))), sin(th) * vxA * vyB / (ay * cos(th)) != 0, ay != 0))) .SubstituteEqLs(vals) .CheckVariable(vyB) .SimplifyEquation() .CheckVariable(vyB) .AssertEqTo( and( vyB == -35.010376910485483, vyB != 0)); DoubleFloat.tolerance = null; } [Fact] public void PSE_5E_P4_9() { // In a local bar, a customer slides an empty beer mug // down the counter for a refill. The bartender is momentarily // distracted and does not see the mug, which slides // off the counter and strikes the floor 1.40 m from the // base of the counter. If the height of the counter is // 0.860 m, (a) with what velocity did the mug leave the // counter and (b) what was the direction of the mug’s // velocity just before it hit the floor? var xA = new Symbol("xA"); var yA = new Symbol("yA"); var xB = new Symbol("xB"); var yB = new Symbol("yB"); var vxA = new Symbol("vxA"); var vyA = new Symbol("vyA"); var vxB = new Symbol("vxB"); var vyB = new Symbol("vyB"); var tAB = new Symbol("tAB"); var ax = new Symbol("ax"); var ay = new Symbol("ay"); var thB = new Symbol("thB"); var vB = new Symbol("vB"); var eqs = and( vxB == vxA + ax * tAB, vyB == vyA + ay * tAB, tan(thB) == vyB / vxB, xB == xA + vxA * tAB + ax * (tAB ^ 2) / 2, yB == yA + vyA * tAB + ay * (tAB ^ 2) / 2, xB != 0 ); var vals = new List() { xA == 0, yA == 0.86, /* vxA */ vyA == 0, xB == 1.4, yB == 0, /* vxB vyB vB thB */ /* tAB */ ax == 0, ay == -9.8 }; var zeros = vals.Where(eq => eq.b == 0).ToList(); DoubleFloat.tolerance = 0.00001; eqs .SubstituteEqLs(zeros) .EliminateVariables(thB, vxB, vyB, tAB) .IsolateVariable(vxA) .LogicalExpand() .AssertEqTo( or( and( vxA == ay * (xB ^ 2) / yA / 4 * sqrt(-8 / ay * (xB ^ -2) * yA), 2 / ay * (xB ^ -2) * yA != 0, xB != 0), and( vxA == -ay * (xB ^ 2) / yA / 4 * sqrt(-8 / ay * (xB ^ -2) * yA), 2 / ay * (xB ^ -2) * yA != 0, xB != 0))) .SubstituteEqLs(vals) .AssertEqTo(or(vxA == -3.3417722634053204, vxA == 3.3417722634053204)); eqs .SubstituteEqLs(zeros) .EliminateVariables(vxB, vyB, tAB, vxA) .LogicalExpand() .CheckVariable(xB) .SimplifyLogical() .IsolateVariable(thB) .AssertEqTo( and( -tan(thB) / ay != 0, thB == new Atan(-2 * yA / xB), xB != 0)) .SubstituteEqLs(vals) .AssertEqTo( and( 0.1020408163265306 * tan(thB) != 0, thB == -0.88760488150470185)); DoubleFloat.tolerance = null; } Func SumDifferenceFormulaFunc = elt => { // sin(u) cos(v) - cos(u) sin(v) -> sin(u - v) if (elt is Sum) { var items = new List(); foreach (var item in (elt as Sum).elts) { if ( item is Product && (item as Product).elts[0] == -1 && (item as Product).elts[1] is Cos && (item as Product).elts[2] is Sin ) { var u_ = ((item as Product).elts[1] as Cos).args[0]; var v_ = ((item as Product).elts[2] as Sin).args[0]; Func match = obj => obj is Product && (obj as Product).elts[0] is Cos && (obj as Product).elts[1] is Sin && ((obj as Product).elts[1] as Sin).args[0] == u_ && ((obj as Product).elts[0] as Cos).args[0] == v_; if (items.Any(obj => match(obj))) { items = items.Where(obj => match(obj) == false).ToList(); items.Add(sin(u_ - v_)); } else items.Add(item); } else items.Add(item); } return Sum.FromRange(items).Simplify(); } return elt; }; [Fact] public void SumDifferenceFormulaFuncTest() { var u = new Symbol("u"); var v = new Symbol("v"); (sin(u) * cos(v) - cos(u) * sin(v)) .DeepSelect(SumDifferenceFormulaFunc) .AssertEqTo(sin(u - v)); } Func SumDifferenceFormulaAFunc = elt => { if (elt is Sum) { var items = new List(); foreach (var item in (elt as Sum).elts) { if ( item is Product && (item as Product).elts[0] is Cos && (item as Product).elts[1] is Sin ) { var u_ = ((item as Product).elts[0] as Cos).args[0]; var v_ = ((item as Product).elts[1] as Sin).args[0]; Func match = obj => obj is Product && (obj as Product).elts[0] is Cos && (obj as Product).elts[1] is Sin && ((obj as Product).elts[1] as Sin).args[0] == u_ && ((obj as Product).elts[0] as Cos).args[0] == v_; if (items.Any(obj => match(obj))) { items = items.Where(obj => match(obj) == false).ToList(); items.Add(sin(u_ + v_)); } else items.Add(item); } else items.Add(item); } return Sum.FromRange(items).Simplify(); } return elt; }; [Fact] public void SumDifferenceFormulaAFuncTest() { // sin(u) cos(v) + cos(u) sin(v) -> sin(u + v) var u = new Symbol("u"); var v = new Symbol("v"); (sin(u) * cos(v) + cos(u) * sin(v)) .DeepSelect(SumDifferenceFormulaAFunc) .AssertEqTo(sin(u + v)); } Func DoubleAngleFormulaFunc = elt => { // sin(u) cos(u) -> sin(2 u) / 2 if (elt is Product) { var items = new List(); foreach (var item in (elt as Product).elts) { if (item is Sin) { var sym = (item as Sin).args.First(); if (items.Any(obj => (obj is Cos) && (obj as Cos).args.First() == sym)) { items = items.Where(obj => ((obj is Cos) && (obj as Cos).args.First() == sym) == false).ToList(); items.Add(sin(2 * sym) / 2); } else items.Add(item); } else if (item is Cos) { var sym = (item as Cos).args.First(); if (items.Any(obj => (obj is Sin) && (obj as Sin).args.First() == sym)) { items = items.Where(obj => ((obj is Sin) && (obj as Sin).args.First() == sym) == false).ToList(); items.Add(sin(2 * sym) / 2); } else items.Add(item); } else items.Add(item); } return Product.FromRange(items).Simplify(); } return elt; }; Func SinCosToTanFunc = elt => { // sin(x) / cos(x) -> tan(x) if (elt is Product) { if ((elt as Product).elts.Any(obj1 => obj1 is Sin && (elt as Product).elts.Any(obj2 => obj2 == 1 / cos((obj1 as Sin).args[0])))) { var sin_ = (elt as Product).elts.First(obj1 => obj1 is Sin && (elt as Product).elts.Any(obj2 => obj2 == 1 / cos((obj1 as Sin).args[0]))); var arg = (sin_ as Sin).args[0]; return elt * cos(arg) / sin(arg) * tan(arg); } return elt; } return elt; }; [Fact] public void SinCosToTanFuncTest() { var x = new Symbol("x"); var y = new Symbol("y"); (sin(x) / cos(x)).DeepSelect(SinCosToTanFunc) .AssertEqTo(tan(x)); (y * sin(x) / cos(x)).DeepSelect(SinCosToTanFunc) .AssertEqTo(tan(x) * y); (sin(x) * sin(y) / cos(x) / cos(y)) .DeepSelect(SinCosToTanFunc) .DeepSelect(SinCosToTanFunc) .AssertEqTo(tan(x) * tan(y)); } [Fact] public void PSE_5E_P4_11() { // One strategy in a snowball fight is to throw a first snowball // at a high angle over level ground. While your opponent is watching // the first one, you throw a second one at a low angle and timed // to arrive at your opponent before or at the same time as the first one. // Assume both snowballs are thrown with a speed of 25.0 m/s. // The first one is thrown at an angle of 70.0° with respect to the horizontal. // (a) At what angle should the second (lowangle) // snowball be thrown if it is to land at the same // point as the first? // (b) How many seconds later should the second snowball // be thrown if it is to land at the same time as the first? var xA = new Symbol("xA"); var yA = new Symbol("yA"); var vxA = new Symbol("vxA"); var vyA = new Symbol("vyA"); var vA = new Symbol("vA"); var thA = new Symbol("thA"); var xB = new Symbol("xB"); var yB = new Symbol("yB"); var vxB = new Symbol("vxB"); var vyB = new Symbol("vyB"); var tAB = new Symbol("tAB"); var tAC = new Symbol("tAC"); var ax = new Symbol("ax"); var ay = new Symbol("ay"); var Pi = new Symbol("Pi"); var eqs = new And( vxA == vA * cos(thA), vyA == vA * sin(thA), vxB == vxA + ax * tAB, vyB == vyA + ay * tAB, xB == xA + vxA * tAB + ax * (tAB ^ 2) / 2, yB == yA + vyA * tAB + ay * (tAB ^ 2) / 2 ); DoubleFloat.tolerance = 0.00001; { var vals = new List() { xA == 0, yA == 0, /* vxA vyA */ vA == 25.0, /* thA == 70.0, */ /* xB == 20.497, */ /* yB */ /* vxB */ vyB == 0, /* tAB */ ax == 0, ay == -9.8, Pi == Math.PI }; var zeros = vals.Where(eq => eq.b == 0).ToList(); { // thA = ... || thA = ... var expr = eqs .SubstituteEqLs(zeros) .EliminateVariables(yB, vxA, vyA, vxB, tAB) .DeepSelect(DoubleAngleFormulaFunc) .IsolateVariable(thA); // th_delta = ... var th1 = ((expr as Or).args[0] as Equation).b; var th2 = ((expr as Or).args[1] as Equation).b; var th_delta = new Symbol("th_delta"); eqs .Add(th_delta == (th1 - th2).AlgebraicExpand()) .SubstituteEqLs(zeros) .EliminateVariables(yB, vxA, vyA, vxB, tAB) .DeepSelect(DoubleAngleFormulaFunc) .EliminateVariable(xB) .AssertEqTo(th_delta == asin(sin(2 * thA)) - Pi / 2) .SubstituteEq(thA == (70).ToRadians()) .SubstituteEq(Pi == Math.PI) .AssertEqTo(th_delta == -0.87266462599716454) ; } { // tAB = ... var tAB_eq = eqs .SubstituteEqLs(zeros) .EliminateVariables(yB, vxA, vyA, vxB, xB) .IsolateVariable(tAB); and( or(thA == (20).ToRadians(), thA == (70).ToRadians()), tAB_eq, tAC == tAB * 2) .LogicalExpand() .EliminateVariables(thA, tAB) .AssertEqTo(or( tAC == -2 * sin(Pi / 9) * vA / ay, tAC == -2 * sin(7 * Pi / 18) * vA / ay)) .SubstituteEqLs(vals) .AssertEqTo( or( tAC == 1.7450007312534115, tAC == 4.794350106050552)); } } DoubleFloat.tolerance = null; } [Fact] public void PSE_5E_P4_13() { // An artillery shell is fired with an initial velocity of // 300 m/s at 55.0° above the horizontal. It explodes on a // mountainside 42.0 s after firing. What are the x and y // coordinates of the shell where it explodes, relative to its // firing point? var xA = new Symbol("xA"); var yA = new Symbol("yA"); var vxA = new Symbol("vxA"); var vyA = new Symbol("vyA"); var vA = new Symbol("vA"); var thA = new Symbol("thA"); var xB = new Symbol("xB"); var yB = new Symbol("yB"); var vxB = new Symbol("vxB"); var vyB = new Symbol("vyB"); var tAB = new Symbol("tAB"); var ax = new Symbol("ax"); var ay = new Symbol("ay"); var Pi = new Symbol("Pi"); var eqs = and( vxA == vA * cos(thA), vyA == vA * sin(thA), vxB == vxA + ax * tAB, vyB == vyA + ay * tAB, xB == xA + vxA * tAB + ax * (tAB ^ 2) / 2, yB == yA + vyA * tAB + ay * (tAB ^ 2) / 2 ); DoubleFloat.tolerance = 0.00001; { var vals = new List() { xA == 0, yA == 0, /* vxA vyA */ vA == 300.0, thA == (55).ToRadians(), /* xB yB vxB vyB */ tAB == 42, ax == 0, ay == -9.8, Pi == Math.PI }; var zeros = vals.Where(eq => eq.b == 0).ToList(); { eqs .SubstituteEqLs(zeros) .EliminateVariable(vxA) .EliminateVariable(vyA) .AssertEqTo( and( vxB == cos(thA) * vA, vyB == ay * tAB + sin(thA) * vA, xB == cos(thA) * tAB * vA, yB == ay * (tAB ^ 2) / 2 + sin(thA) * tAB * vA)) .SubstituteEqLs(vals) .AssertEqTo( and( vxB == 172.07293090531385, vyB == -165.85438671330249, xB == 7227.0630980231817, yB == 1677.7157580412968)) ; } } DoubleFloat.tolerance = null; } [Fact] public void PSE_5E_P4_15() { // A projectile is fired in such a way that its horizontal // range is equal to three times its maximum height. // // What is the angle of projection? // Give your answer to three significant figures. var xA = new Symbol("xA"); var yA = new Symbol("yA"); var vxA = new Symbol("vxA"); var vyA = new Symbol("vyA"); var vA = new Symbol("vA"); var thA = new Symbol("thA"); var xB = new Symbol("xB"); var yB = new Symbol("yB"); var vxB = new Symbol("vxB"); var vyB = new Symbol("vyB"); var xC = new Symbol("xC"); var yC = new Symbol("yC"); var vxC = new Symbol("vxC"); var vyC = new Symbol("vyC"); var tAB = new Symbol("tAB"); var tBC = new Symbol("tBC"); var ax = new Symbol("ax"); var ay = new Symbol("ay"); var Pi = new Symbol("Pi"); var eqs = and( xC - xA == 3 * yB, tAB == tBC, vxA == vA * cos(thA), vyA == vA * sin(thA), vxB == vxA + ax * tAB, vyB == vyA + ay * tAB, xB == xA + vxA * tAB + ax * (tAB ^ 2) / 2, yB == yA + vyA * tAB + ay * (tAB ^ 2) / 2, vxC == vxB + ax * tBC, vyC == vyB + ay * tBC, xC == xB + vxB * tBC + ax * (tBC ^ 2) / 2, yC == yB + vyB * tBC + ay * (tBC ^ 2) / 2 ); DoubleFloat.tolerance = 0.00001; { var vals = new List() { xA == 0, yA == 0, /* vxA vyA vA thA */ /* xB yB vxB */ vyB == 0, /* tAB tBC */ /* xC */ yC == 0, ax == 0, ay == -9.8, Pi == Math.PI }; var zeros = vals.Where(eq => eq.b == 0).ToList(); eqs .SubstituteEqLs(zeros) .EliminateVariables(xC, tAB, vxA, vyA, vxB, xB, yB, vxC, vyC, tBC) .IsolateVariable(thA) .AssertEqTo(thA == new Atan(new Integer(4) / 3)); } DoubleFloat.tolerance = null; } [Fact] public void PSE_5E_P4_17() { // A cannon with a muzzle speed of 1000 m/s is used to // start an avalanche on a mountain slope. The target is // 2000 m from the cannon horizontally and 800 m above // the cannon. // // At what angle, above the horizontal, should the cannon be fired? var xA = new Symbol("xA"); var yA = new Symbol("yA"); var vxA = new Symbol("vxA"); var vyA = new Symbol("vyA"); var vA = new Symbol("vA"); var thA = new Symbol("thA"); var xB = new Symbol("xB"); var yB = new Symbol("yB"); var vxB = new Symbol("vxB"); var vyB = new Symbol("vyB"); var xC = new Symbol("xC"); var yC = new Symbol("yC"); var vxC = new Symbol("vxC"); var vyC = new Symbol("vyC"); var tAB = new Symbol("tAB"); var tBC = new Symbol("tBC"); var ax = new Symbol("ax"); var ay = new Symbol("ay"); var Pi = new Symbol("Pi"); var phi = new Symbol("phi"); var eqs = and( vxA == vA * cos(thA), vyA == vA * sin(thA), vxB == vxA + ax * tAB, vyB == vyA + ay * tAB, xB == xA + vxA * tAB + ax * (tAB ^ 2) / 2, yB == yA + vyA * tAB + ay * (tAB ^ 2) / 2 ); DoubleFloat.tolerance = 0.00001; { var vals = new List() { xA == 0, yA == 0, /* vxA vyA */ vA == 1000, /* thA */ xB == 2000, yB == 800.0, /* vxB vyB */ /* tAB */ ax == 0, ay == -9.8, Pi == Math.PI }; var zeros = vals.Where(eq => eq.b == 0).ToList(); { eqs .SubstituteEqLs(zeros) .EliminateVariables(vxA, vyA, vxB, vyB, tAB) .MultiplyBothSidesBy(cos(thA) ^ 2).AlgebraicExpand() .Substitute(cos(thA) ^ 2, (1 + cos(2 * thA)) / 2) .DeepSelect(DoubleAngleFormulaFunc).AlgebraicExpand() .AddToBothSides(-sin(2 * thA) * xB / 2) .AddToBothSides(-yB / 2) .MultiplyBothSidesBy(2 / xB).AlgebraicExpand() // yB / xB = tan(phi) // yB / xB = sin(phi) / cos(phi) // phi = atan(yB / xB) .Substitute(cos(2 * thA) * yB / xB, cos(2 * thA) * sin(phi) / cos(phi)) .MultiplyBothSidesBy(cos(phi)).AlgebraicExpand() .DeepSelect(SumDifferenceFormulaFunc) .IsolateVariable(thA) .Substitute(phi, new Atan(yB / xB).Simplify()) .AssertEqTo( or( thA == -(asin(ay * cos(atan(yB / xB)) * (vA ^ -2) * xB + -1 * cos(atan(yB / xB)) * yB / xB) - atan(yB / xB)) / 2, thA == -(-asin(ay * cos(atan(yB / xB)) * (vA ^ -2) * xB - cos(atan(yB / xB)) * yB / xB) - atan(yB / xB) + Pi) / 2)) .SubstituteEqLs(vals) .AssertEqTo( or( thA == 0.39034573609628065, thA == -1.5806356857788124)) ; } } DoubleFloat.tolerance = null; } [Fact] public void PSE_5E_P4_19() { // A placekicker must kick a football from a point 36.0 m // (about 40 yards) from the goal, and half the crowd // hopes the ball will clear the crossbar, which is 3.05 m // high. When kicked, the ball leaves the ground with a // speed of 20.0 m/s at an angle of 53.0° to the horizontal. // // (a) By how much does the ball clear or fall short of // clearing the crossbar ? // // (b) Does the ball approach the crossbar while still // rising or while falling ? Func sqrt = obj => obj ^ (new Integer(1) / 2); var xA = new Symbol("xA"); var yA = new Symbol("yA"); var vxA = new Symbol("vxA"); var vyA = new Symbol("vyA"); var vA = new Symbol("vA"); var thA = new Symbol("thA"); var xB = new Symbol("xB"); var yB = new Symbol("yB"); var vxB = new Symbol("vxB"); var vyB = new Symbol("vyB"); var tAB = new Symbol("tAB"); var ax = new Symbol("ax"); var ay = new Symbol("ay"); var Pi = new Symbol("Pi"); var cleared_by = new Symbol("cleared_by"); var goal_height = new Symbol("goal_height"); var eqs = and( vxA == vA * cos(thA), vyA == vA * sin(thA), vxB == vxA + ax * tAB, vyB == vyA + ay * tAB, xB == xA + vxA * tAB + ax * (tAB ^ 2) / 2, yB == yA + vyA * tAB + ay * (tAB ^ 2) / 2, cleared_by == yB - goal_height ); DoubleFloat.tolerance = 0.00001; { var vals = new List() { xA == 0, yA == 0, /* vxA vyA */ vA == 20, thA == (53).ToRadians(), xB == 36, /* yB */ /* vxB vyB */ /* tAB */ ax == 0, ay == -9.8, Pi == Math.PI, goal_height == 3.05 }; var zeros = vals.Where(eq => eq.b == 0).ToList(); { eqs .SubstituteEqLs(zeros) .EliminateVariables(vxA, vyA, vxB, vyB, tAB, yB) .AssertEqTo( cleared_by == -goal_height + sin(thA) / cos(thA) * xB + ay / 2 * (cos(thA) ^ -2) * (vA ^ -2) * (xB ^ 2) ) .SubstituteEqLs(vals) .AssertEqTo(cleared_by == 0.88921618776713007); } { eqs .SubstituteEqLs(zeros) .EliminateVariables(cleared_by, vxA, vyA, vxB, tAB, yB) .IsolateVariable(vyB) .AssertEqTo(vyB == sin(thA) * vA + ay / cos(thA) / vA * xB) .SubstituteEqLs(vals) .AssertEqTo(vyB == -13.338621888454744); } } DoubleFloat.tolerance = null; } [Fact] public void PSE_5E_P4_21() { // A firefighter a distance d from a burning building directs // a stream of water from a fire hose at angle θi above // the horizontal as in Figure P4.20.If the initial speed of // the stream is vi, at what height h does the water strike // the building? var xA = new Symbol("xA"); var yA = new Symbol("yA"); var vxA = new Symbol("vxA"); var vyA = new Symbol("vyA"); var vA = new Symbol("vA"); var thA = new Symbol("thA"); var xB = new Symbol("xB"); var yB = new Symbol("yB"); var vxB = new Symbol("vxB"); var vyB = new Symbol("vyB"); var tAB = new Symbol("tAB"); var ax = new Symbol("ax"); var ay = new Symbol("ay"); var Pi = new Symbol("Pi"); var d = new Symbol("d"); var thi = new Symbol("thi"); var vi = new Symbol("vi"); var h = new Symbol("h"); var eqs = and( vxA == vA * cos(thA), vyA == vA * sin(thA), vxB == vxA + ax * tAB, vyB == vyA + ay * tAB, xB == xA + vxA * tAB + ax * (tAB ^ 2) / 2, yB == yA + vyA * tAB + ay * (tAB ^ 2) / 2 ); DoubleFloat.tolerance = 0.00001; { var vals = new List() { xA == 0, yA == 0, /* vxA vyA */ vA == vi, thA == thi, xB == d, yB == h, /* vxB vyB */ /* tAB */ ax == 0, ay == -9.8, Pi == Math.PI }; var zeros = vals.Where(eq => eq.b == 0).ToList(); { eqs .SubstituteEqLs(zeros) .EliminateVariables(vxA, vyA, vxB, vyB, tAB) .SubstituteEqLs(vals.Where(eq => eq.b is Symbol).ToList()) .AssertEqTo( h == d * sin(thi) / cos(thi) + ay * (d ^ 2) / (cos(thi) ^ 2) / (vi ^ 2) / 2 ); } } DoubleFloat.tolerance = null; } [Fact] public void PSE_5E_P4_23() { // A basketball star covers 2.80 m horizontally in a jump to // dunk the ball. His motion through space can be modeled as // that of a particle at a point called his center of mass. // His center of mass is at elevation 1.02 m when he leaves // the floor. It reaches a maximum height of 1.85 m above // the floor and is at elevation 0.900 m when he touches down // again. // Determine: // (a) his time of flight (his “hang time”) // (b) his horizontal and (c) vertical velocity components at the instant of takeoff // (d) his takeoff angle. // (e) For comparison, determine the hang time of a // whitetail deer making a jump with center-of-mass elevations // y_i = 1.20 m // y_max = 2.50 m // y_f = 0.700 m var xA = new Symbol("xA"); var yA = new Symbol("yA"); var vxA = new Symbol("vxA"); var vyA = new Symbol("vyA"); var vA = new Symbol("vA"); var thA = new Symbol("thA"); var xB = new Symbol("xB"); var yB = new Symbol("yB"); var vxB = new Symbol("vxB"); var vyB = new Symbol("vyB"); var tAB = new Symbol("tAB"); var xC = new Symbol("xC"); var yC = new Symbol("yC"); var vxC = new Symbol("vxC"); var vyC = new Symbol("vyC"); var tBC = new Symbol("tBC"); var tAC = new Symbol("tAC"); var ax = new Symbol("ax"); var ay = new Symbol("ay"); var Pi = new Symbol("Pi"); var eqs = and( //vxA == vA * cos(thA), //vyA == vA * sin(thA), vxB == vxA + ax * tAB, vyB == vyA + ay * tAB, xB == xA + vxA * tAB + ax * (tAB ^ 2) / 2, yB == yA + vyA * tAB + ay * (tAB ^ 2) / 2, vxC == vxB + ax * tBC, vyC == vyB + ay * tBC, xC == xB + vxB * tBC + ax * (tBC ^ 2) / 2, yC == yB + vyB * tBC + ay * (tBC ^ 2) / 2, tAC == tAB + tBC, // vyA / vxA == tan(thA), tan(thA) == vyA / vxA, ay != 0 ); DoubleFloat.tolerance = 0.00001; { var vals = new List() { xA == 0, yA == 1.02, /* vxA vyA vA thA */ /* xB */ yB == 1.85, /* vxB */ vyB == 0, xC == 2.80, yC == 0.9, /* vxC vyC */ /* tAB tBC */ ax == 0, ay == -9.8, Pi == Math.PI }; var zeros = vals.Where(eq => eq.b == 0).ToList(); { eqs .SubstituteEqLs(zeros) .EliminateVariables(thA, vxB, xB, vxC, vyC, vxA, vyA, tAB) .CheckVariable(ay).SimplifyEquation().SimplifyLogical() .EliminateVariable(tBC) .LogicalExpand().SimplifyEquation().CheckVariable(ay).SimplifyLogical() .AssertEqTo( or( and(ay != 0, tAC == (ay ^ -1) * sqrt(-2 * ay * (-1 * yA + yB)) + -1 * (ay ^ -1) * sqrt(2 * ay * (-1 * yB + yC))), and(ay != 0, tAC == (ay ^ -1) * sqrt(-2 * ay * (-1 * yA + yB)) + (ay ^ -1) * sqrt(2 * ay * (-1 * yB + yC))), and(ay != 0, tAC == -1 * (ay ^ -1) * sqrt(-2 * ay * (-1 * yA + yB)) + -1 * (ay ^ -1) * sqrt(2 * ay * (-1 * yB + yC))), and(ay != 0, tAC == -1 * (ay ^ -1) * sqrt(-2 * ay * (-1 * yA + yB)) + (ay ^ -1) * sqrt(2 * ay * (-1 * yB + yC))))) .SubstituteEqLs(vals) .AssertEqTo( or( tAC == 0.028747849043843032, tAC == -0.85188272280886768, tAC == 0.85188272280886768, tAC == -0.028747849043843032)); } { eqs .SubstituteEqLs(zeros) .EliminateVariables(thA, vxB, vxC, xB) .IsolateVariable(vxA) .EliminateVariables(tAC, vyC, tAB, vyA) .SimplifyEquation().CheckVariable(ay) .EliminateVariable(tBC) .LogicalExpand().SimplifyEquation().CheckVariable(ay).SimplifyLogical() .AssertEqTo( or( and(ay != 0, vxA == xC * ((-1 * sqrt(-2 * (ay ^ -1) * (-1 * yA + yB)) + -1 * (ay ^ -1) * sqrt(2 * ay * (-1 * yB + yC))) ^ -1)), and(ay != 0, vxA == xC * ((-1 * sqrt(-2 * (ay ^ -1) * (-1 * yA + yB)) + (ay ^ -1) * sqrt(2 * ay * (-1 * yB + yC))) ^ -1)), and(ay != 0, vxA == xC * ((sqrt(-2 * (ay ^ -1) * (-1 * yA + yB)) + -1 * (ay ^ -1) * sqrt(2 * ay * (-1 * yB + yC))) ^ -1)), and(ay != 0, vxA == xC * ((sqrt(-2 * (ay ^ -1) * (-1 * yA + yB)) + (ay ^ -1) * sqrt(2 * ay * (-1 * yB + yC))) ^ -1)))) .SubstituteEqLs(vals) .AssertEqTo( or( vxA == 97.398591307814215, vxA == -3.286837407346058, vxA == 3.286837407346058, vxA == -97.398591307814215)); } { eqs .SubstituteEqLs(zeros) .EliminateVariables(thA, vxA, vxC, vyC, vxB, xB, tAB, tAC, tBC) .SimplifyEquation().CheckVariable(ay).SimplifyLogical() .IsolateVariable(vyA) .LogicalExpand().SimplifyEquation().CheckVariable(ay) .AssertEqTo( or( and(ay != 0, vyA == ay * sqrt(-2 * (ay ^ -1) * (-1 * yA + yB))), and(ay != 0, vyA == -1 * ay * sqrt(-2 * (ay ^ -1) * (-1 * yA + yB))))) .SubstituteEqLs(vals) .AssertEqTo( or( vyA == -4.0333608814486217, vyA == 4.0333608814486217)); } { eqs .SubstituteEqLs(zeros) .EliminateVariables(vxA, vyA, vxB, xB, vxC, tBC, tAB, vyC, tAC) .LogicalExpand() .SimplifyEquation() .SimplifyLogical() .CheckVariable(ay) .AssertEqTo( or( and(ay != 0, tan(thA) == -1 * (xC ^ -1) * sqrt(-2 * ay * (-1 * yA + yB)) * ((ay ^ -1) * sqrt(-2 * ay * (-1 * yA + yB)) + -1 * sqrt(2 * (ay ^ -1) * (-1 * yB + yC)))), and(ay != 0, tan(thA) == -1 * (xC ^ -1) * sqrt(-2 * ay * (-1 * yA + yB)) * ((ay ^ -1) * sqrt(-2 * ay * (-1 * yA + yB)) + sqrt(2 * (ay ^ -1) * (-1 * yB + yC)))), and(ay != 0, tan(thA) == (xC ^ -1) * sqrt(-2 * ay * (-1 * yA + yB)) * (-1 * (ay ^ -1) * sqrt(-2 * ay * (-1 * yA + yB)) + -1 * sqrt(2 * (ay ^ -1) * (-1 * yB + yC)))), and(ay != 0, tan(thA) == (xC ^ -1) * sqrt(-2 * ay * (-1 * yA + yB)) * (-1 * (ay ^ -1) * sqrt(-2 * ay * (-1 * yA + yB)) + sqrt(2 * (ay ^ -1) * (-1 * yB + yC)))) )) .IsolateVariable(thA) .SubstituteEqLs(vals) .AssertEqTo( or( thA == 0.88702813023277882, thA == -0.041387227947930878, thA == -0.041387227947930878, thA == 0.88702813023277882)); } } DoubleFloat.tolerance = null; } [Fact] public void PSE_5E_E5_1() { // A hockey puck having a mass of 0.30 kg slides on the horizontal, // frictionless surface of an ice rink. Two forces act on // the puck, as shown in Figure 5.5.The force F1 has a magnitude // of 5.0 N, and the force F2 has a magnitude of 8.0 N. // Determine both the magnitude and the direction of the puck’s acceleration. // Determine the components of a third force that, // when applied to the puck, causes it to have zero acceleration. var F = new Symbol("F"); var th = new Symbol("th"); var Fx = new Symbol("Fx"); var Fy = new Symbol("Fy"); var F1 = new Symbol("F1"); var th1 = new Symbol("th1"); var F1x = new Symbol("F1x"); var F1y = new Symbol("F1y"); var F2 = new Symbol("F2"); var th2 = new Symbol("th2"); var F2x = new Symbol("F2x"); var F2y = new Symbol("F2y"); var F3 = new Symbol("F3"); var th3 = new Symbol("th3"); var F3x = new Symbol("F3x"); var F3y = new Symbol("F3y"); var a = new Symbol("a"); var ax = new Symbol("ax"); var ay = new Symbol("ay"); var m = new Symbol("m"); var Pi = new Symbol("Pi"); var eqs = and( Fx == F * cos(th), Fy == F * sin(th), Fx == ax * m, Fy == ay * m, Fx == F1x + F2x + F3x, Fy == F1y + F2y + F3y, F1x == F1 * cos(th1), F1y == F1 * sin(th1), F2x == F2 * cos(th2), F2y == F2 * sin(th2), F3x == F3 * cos(th3), F3y == F3 * sin(th3), a == sqrt((ax ^ 2) + (ay ^ 2)) ); DoubleFloat.tolerance = 0.00001; { var vals = new List() { m == 0.3, F1 == 5.0, th1 == (-20).ToRadians(), F2 == 8.0, th2 == (60).ToRadians(), F3 == 0, Pi == Math.PI }; var zeros = vals.Where(eq => eq.b == 0).ToList(); // a { eqs .SubstituteEqLs(zeros) .EliminateVariables(ax, ay, Fx, Fy, F, F1x, F1y, F2x, F2y, F3x, F3y) .DeepSelect(SinCosToTanFunc) .EliminateVariable(th) .AssertEqTo( a == sqrt( ((cos(th1) * F1 + cos(th2) * F2) ^ 2) * (m ^ -2) + (cos(atan(((cos(th1) * F1 + cos(th2) * F2) ^ -1) * (F1 * sin(th1) + F2 * sin(th2)))) ^ -2) * ((cos(th1) * F1 + cos(th2) * F2) ^ 2) * (m ^ -2) * (sin(atan(((cos(th1) * F1 + cos(th2) * F2) ^ -1) * (F1 * sin(th1) + F2 * sin(th2)))) ^ 2)) ) .SubstituteEqLs(vals) .Substitute(3, 3.0) //.DispLong() .AssertEqTo(a == 33.811874017759315); } // th { eqs .SubstituteEqLs(zeros) .EliminateVariables(a, F, Fx, Fy, ax, ay, F1x, F1y, F2x, F2y, F3x, F3y) .DeepSelect(SinCosToTanFunc) .IsolateVariable(th) .AssertEqTo( th == atan((F1 * sin(th1) + F2 * sin(th2)) / (cos(th1) * F1 + cos(th2) * F2)) ) .SubstituteEqLs(vals) .Substitute(3, 3.0) .AssertEqTo(th == 0.54033704850428876); } } { var vals = new List() { m == 0.3, F1 == 5.0, th1 == (-20).ToRadians(), F2 == 8.0, th2 == (60).ToRadians(), ax == 0, ay == 0, Pi == Math.PI }; var zeros = vals.Where(eq => eq.b == 0).ToList(); // F3x { eqs .SubstituteEqLs(zeros) .EliminateVariables(F3, th3, F3y, F1x, F2x, Fx, F, Fy, F1y, F2y, a) .IsolateVariable(F3x) .AssertEqTo(F3x == -1 * cos(th1) * F1 + -1 * cos(th2) * F2) .SubstituteEqLs(vals) .AssertEqTo(F3x == -8.6984631039295444); } // F3y { eqs .SubstituteEqLs(zeros) .EliminateVariables(F3, th3, F3x, F1x, F2x, Fx, F, Fy, F1y, F2y, a) .IsolateVariable(F3y) .AssertEqTo(F3y == -1 * F1 * sin(th1) + -1 * F2 * sin(th2)) .SubstituteEqLs(vals) // .DispLong() .Substitute(3, 3.0) .AssertEqTo(F3y == -5.2181025136471657); } } } [Fact] public void PSE_5E_E5_4() { // A traffic light weighing 125 N hangs from a cable tied to two // other cables fastened to a support. The upper cables make // angles of 37.0° and 53.0° with the horizontal. Find the tension // in the three cables. var F = new Symbol("F"); // total force magnitude var th = new Symbol("th"); // total force direction var Fx = new Symbol("Fx"); // total force x-component var Fy = new Symbol("Fy"); // total force y-component var F1 = new Symbol("F1"); var th1 = new Symbol("th1"); var F1x = new Symbol("F1x"); var F1y = new Symbol("F1y"); var F2 = new Symbol("F2"); var th2 = new Symbol("th2"); var F2x = new Symbol("F2x"); var F2y = new Symbol("F2y"); var F3 = new Symbol("F3"); var th3 = new Symbol("th3"); var F3x = new Symbol("F3x"); var F3y = new Symbol("F3y"); var a = new Symbol("a"); var ax = new Symbol("ax"); var ay = new Symbol("ay"); var m = new Symbol("m"); var Pi = new Symbol("Pi"); var eqs = and( Fx == F * cos(th), Fy == F * sin(th), Fx == ax * m, Fy == ay * m, Fx == F1x + F2x + F3x, Fy == F1y + F2y + F3y, F1x == F1 * cos(th1), F1y == F1 * sin(th1), F2x == F2 * cos(th2), F2y == F2 * sin(th2), F3x == F3 * cos(th3), F3y == F3 * sin(th3), a == sqrt((ax ^ 2) + (ay ^ 2)) ); DoubleFloat.tolerance = 0.00001; { var vals = new List() { // m /* F1 */ th1 == (180 - 37).ToRadians(), // F1x F1y /* F2 */ th2 == (53).ToRadians(), // F2x F2y F3 == 125, th3 == (270).ToRadians(), // F3x F3y ax == 0, ay == 0, Pi == Math.PI }; var zeros = vals.Where(eq => eq.b == 0).ToList(); // F1 { eqs .SubstituteEqLs(zeros) .EliminateVariables(Fx, Fy, F, F1x, F1y, F2x, F2y, F2, F3x, F3y, a) .IsolateVariable(F1) .AssertEqTo(F1 == (F3 * sin(th3) - cos(th3) * F3 * sin(th2) / cos(th2)) / (cos(th1) * sin(th2) / cos(th2) - sin(th1))) .SubstituteEqLs(vals) .AssertEqTo(F1 == 75.226877894006023); } // F2 { eqs .SubstituteEqLs(zeros) .EliminateVariables(Fx, Fy, F, F1x, F1y, F2x, F2y, F1, F3x, F3y, a) .IsolateVariable(F2) .AssertEqTo(F2 == (cos(th3) * F3 * sin(th1) / cos(th1) - F3 * sin(th3)) / (sin(th2) - cos(th2) * sin(th1) / cos(th1))) .SubstituteEqLs(vals) .AssertEqTo(F2 == 99.829438755911582); } } } [Fact] public void PSE_5E_E5_6() { // A crate of mass m is placed on a frictionless inclined plane of // angle θ. (a) Determine the acceleration of the crate after it is // released. // (b) Suppose the crate is released from rest at the top of // the incline, and the distance from the front edge of the crate // to the bottom is d. How long does it take the front edge to // reach the bottom, and what is its speed just as it gets there? var F = new Symbol("F"); // total force magnitude var th = new Symbol("th"); // total force direction var Fx = new Symbol("Fx"); // total force x-component var Fy = new Symbol("Fy"); // total force y-component var F1 = new Symbol("F1"); var th1 = new Symbol("th1"); var F1x = new Symbol("F1x"); var F1y = new Symbol("F1y"); var F2 = new Symbol("F2"); var th2 = new Symbol("th2"); var F2x = new Symbol("F2x"); var F2y = new Symbol("F2y"); //var F3 = new Symbol("F3"); //var th3 = new Symbol("th3"); //var F3x = new Symbol("F3x"); //var F3y = new Symbol("F3y"); var a = new Symbol("a"); var ax = new Symbol("ax"); var ay = new Symbol("ay"); var m = new Symbol("m"); var n = new Symbol("n"); var g = new Symbol("g"); var incline = new Symbol("incline"); var Pi = new Symbol("Pi"); var xA = new Symbol("xA"); var yA = new Symbol("yA"); var vxA = new Symbol("vxA"); var vyA = new Symbol("vyA"); var vA = new Symbol("vA"); var thA = new Symbol("thA"); var xB = new Symbol("xB"); var yB = new Symbol("yB"); var vxB = new Symbol("vxB"); var vyB = new Symbol("vyB"); var tAB = new Symbol("tAB"); var d = new Symbol("d"); var eqs = and( Fx == F * cos(th), Fy == F * sin(th), Fx == ax * m, Fy == ay * m, Fx == F1x + F2x, //+ F3x, Fy == F1y + F2y, //+ F3y, F1x == F1 * cos(th1), F1y == F1 * sin(th1), F2x == F2 * cos(th2), F2y == F2 * sin(th2), //F3x == F3 * cos(th3), F3y == F3 * sin(th3), a == sqrt((ax ^ 2) + (ay ^ 2)), xB == xA + vxA * tAB + ax * (tAB ^ 2) / 2, vxB == vxA + ax * tAB, d != 0 ); DoubleFloat.tolerance = 0.00001; { var vals = new List() { // m F1 == n, th1 == 90 * Pi / 180, // F1x F1y F2 == m * g, th2 == 270 * Pi / 180 + incline, // F2x F2y //F3 == 125, th3 == (270).ToRadians(), // F3x F3y /* ax */ ay == 0, // Pi == Math.PI xA == 0, xB == d, vxA == 0 }; var zeros = vals.Where(eq => eq.b == 0).ToList(); // ax { eqs .SubstituteEqLs(zeros) .EliminateVariables(a, F) .DeepSelect(SinCosToTanFunc) .EliminateVariables(th, Fx, F1x, F2x, Fy, F1y, F2y, vxB, xB) .SubstituteEqLs(vals) .EliminateVariable(n) .IsolateVariable(ax) .AssertEqTo( and( ax == g * sin(incline), d != 0)); } // tAB { eqs .SubstituteEqLs(zeros) .EliminateVariables(a, F) .DeepSelect(SinCosToTanFunc) .EliminateVariables(th, Fx, F1x, F2x, Fy, F1y, F2y, ax, vxB) .SubstituteEqLs(vals) .EliminateVariable(n) .IsolateVariable(tAB).LogicalExpand().CheckVariable(d) .AssertEqTo( or( and( tAB == -sqrt(2 * d * g * sin(incline)) / sin(incline) / g, -g * sin(incline) / 2 != 0, d != 0), and( tAB == sqrt(2 * d * g * sin(incline)) / sin(incline) / g, -g * sin(incline) / 2 != 0, d != 0)) ); } // vxB { eqs .SubstituteEqLs(zeros) .EliminateVariables(a, F) .DeepSelect(SinCosToTanFunc) .EliminateVariables(th, Fx, F1x, F2x, Fy, F1y, F2y, ax, tAB) .SubstituteEqLs(vals) .CheckVariable(d) .EliminateVariable(n) .AssertEqTo( or( and( -g * sin(incline) / 2 != 0, vxB == -sqrt(2 * d * g * sin(incline)), d != 0 ), and( -g * sin(incline) / 2 != 0, vxB == sqrt(2 * d * g * sin(incline)), d != 0)) ); } } } [Fact] public void PSE_5E_E5_9() { // When two objects of unequal mass are hung vertically over a // frictionless pulley of negligible mass, as shown in Figure // 5.15a, the arrangement is called an Atwood machine. The device // is sometimes used in the laboratory to measure the freefall // acceleration. // // Determine the magnitude of the acceleration of the two // objects and the tension in the lightweight cord. var F_m1 = new Symbol("F_m1"); // total force on mass 1 var F_m2 = new Symbol("F_m2"); // total force on mass 2 var F1_m1 = new Symbol("F1_m1"); // force 1 on mass 1 var F2_m1 = new Symbol("F2_m1"); // force 2 on mass 1 var F1_m2 = new Symbol("F1_m2"); // force 1 on mass 2 var F2_m2 = new Symbol("F2_m2"); // force 2 on mass 2 var m1 = new Symbol("m1"); var m2 = new Symbol("m2"); var a = new Symbol("a"); var T = new Symbol("T"); var g = new Symbol("g"); var eqs = and( F_m1 == F1_m1 - F2_m1, F_m2 == F2_m2 - F1_m2, F_m1 == m1 * a, F_m2 == m2 * a, F1_m1 == T, F2_m1 == m1 * g, F1_m2 == T, F2_m2 == m2 * g ); DoubleFloat.tolerance = 0.00001; { var vals = new List() { m1 == 2.0, m2 == 4.0, g == 9.8 }; var zeros = vals.Where(eq => eq.b == 0).ToList(); // a { eqs .EliminateVariables(F_m1, F_m2, F2_m1, F2_m2, F1_m1, F1_m2, T) .IsolateVariable(a) .AssertEqTo( a == (-1 * g * m1 + g * m2) / (m1 + m2) ) .SubstituteEqLs(vals) .AssertEqTo(a == 3.2666666666666666); } // T { eqs .EliminateVariables(F_m1, F_m2, F2_m1, F2_m2, F1_m1, F1_m2, a) .IsolateVariable(T) .AssertEqTo( T == 2 * g * m2 / (1 + m2 / m1) ) .SubstituteEqLs(vals) .AssertEqTo( T == 26.133333333333333 ); } } } [Fact] public void PSE_5E_E5_10() { // Acceleration of Two Objects Connected by a Cord // // A ball of mass m1 and a block of mass m2 are attached by a // lightweight cord that passes over a frictionless pulley of // negligible mass, as shown in Figure 5.16a. The block lies // on a frictionless incline of angle th. Find the magnitude // of the acceleration of the two objects and the tension in the cord. //////////////////////////////////////////////////////////////////////////////// var F1_m1 = new Symbol("F1_m1"); // force 1 on mass 1 var F2_m1 = new Symbol("F2_m1"); // force 2 on mass 1 var F3_m1 = new Symbol("F3_m1"); // force 3 on mass 1 var th1_m1 = new Symbol("th1_m1"); // direction of force 1 on mass 1 var th2_m1 = new Symbol("th2_m1"); // direction of force 2 on mass 1 var th3_m1 = new Symbol("th3_m1"); // direction of force 3 on mass 1 var F1x_m1 = new Symbol("F1x_m1"); // x-component of force 1 on mass 1 var F2x_m1 = new Symbol("F2x_m1"); // x-component of force 2 on mass 1 var F3x_m1 = new Symbol("F3x_m1"); // x-component of force 3 on mass 1 var F1y_m1 = new Symbol("F1y_m1"); // y-component of force 1 on mass 1 var F2y_m1 = new Symbol("F2y_m1"); // y-component of force 2 on mass 1 var F3y_m1 = new Symbol("F3y_m1"); // y-component of force 3 on mass 1 var Fx_m1 = new Symbol("Fx_m1"); // x-component of total force on mass 1 var Fy_m1 = new Symbol("Fy_m1"); // y-component of total force on mass 1 var ax_m1 = new Symbol("ax_m1"); // x-component of acceleration of mass 1 var ay_m1 = new Symbol("ay_m1"); // y-component of acceleration of mass 1 var m1 = new Symbol("m1"); //////////////////////////////////////////////////////////////////////////////// var F1_m2 = new Symbol("F1_m2"); // force 1 on mass 2 var F2_m2 = new Symbol("F2_m2"); // force 2 on mass 2 var F3_m2 = new Symbol("F3_m2"); // force 3 on mass 2 var th1_m2 = new Symbol("th1_m2"); // direction of force 1 on mass 2 var th2_m2 = new Symbol("th2_m2"); // direction of force 2 on mass 2 var th3_m2 = new Symbol("th3_m2"); // direction of force 3 on mass 2 var F1x_m2 = new Symbol("F1x_m2"); // x-component of force 1 on mass 2 var F2x_m2 = new Symbol("F2x_m2"); // x-component of force 2 on mass 2 var F3x_m2 = new Symbol("F3x_m2"); // x-component of force 3 on mass 2 var F1y_m2 = new Symbol("F1y_m2"); // y-component of force 1 on mass 2 var F2y_m2 = new Symbol("F2y_m2"); // y-component of force 2 on mass 2 var F3y_m2 = new Symbol("F3y_m2"); // y-component of force 3 on mass 2 var Fx_m2 = new Symbol("Fx_m2"); // x-component of total force on mass 2 var Fy_m2 = new Symbol("Fy_m2"); // y-component of total force on mass 2 var ax_m2 = new Symbol("ax_m2"); // x-component of acceleration of mass 2 var ay_m2 = new Symbol("ay_m2"); // y-component of acceleration of mass 2 var m2 = new Symbol("m2"); //////////////////////////////////////////////////////////////////////////////// var incline = new Symbol("incline"); var T = new Symbol("T"); // tension in cable var g = new Symbol("g"); // gravity var n = new Symbol("n"); // normal force on block var a = new Symbol("a"); var Pi = new Symbol("Pi"); var eqs = and( ax_m2 == ay_m1, // the block moves right as the ball moves up //////////////////////////////////////////////////////////////////////////////// F1x_m1 == F1_m1 * cos(th1_m1), F2x_m1 == F2_m1 * cos(th2_m1), F3x_m1 == F3_m1 * cos(th3_m1), F1y_m1 == F1_m1 * sin(th1_m1), F2y_m1 == F2_m1 * sin(th2_m1), F3y_m1 == F3_m1 * sin(th3_m1), Fx_m1 == F1x_m1 + F2x_m1 + F3x_m1, Fy_m1 == F1y_m1 + F2y_m1 + F3y_m1, Fx_m1 == m1 * ax_m1, Fy_m1 == m1 * ay_m1, //////////////////////////////////////////////////////////////////////////////// F1x_m2 == F1_m2 * cos(th1_m2), F2x_m2 == F2_m2 * cos(th2_m2), F3x_m2 == F3_m2 * cos(th3_m2), F1y_m2 == F1_m2 * sin(th1_m2), F2y_m2 == F2_m2 * sin(th2_m2), F3y_m2 == F3_m2 * sin(th3_m2), Fx_m2 == F1x_m2 + F2x_m2 + F3x_m2, Fy_m2 == F1y_m2 + F2y_m2 + F3y_m2, Fx_m2 == m2 * ax_m2, Fy_m2 == m2 * ay_m2, //////////////////////////////////////////////////////////////////////////////// a == ax_m2 ); DoubleFloat.tolerance = 0.00001; { var vals = new List() { ax_m1 == 0, // ball moves vertically ay_m2 == 0, // block moves horizontally F1_m1 == T, F2_m1 == m1 * g, F3_m1 == 0, th1_m1 == 90 * Pi / 180, // force 1 is straight up th2_m1 == 270 * Pi / 180, // force 2 is straight down F1_m2 == n, F2_m2 == T, F3_m2 == m2 * g, th1_m2 == 90 * Pi / 180, // force 1 is straight up th2_m2 == 180 * Pi / 180, // force 2 is straight down th3_m2 == 270 * Pi / 180 + incline // force 3 direction }; var zeros = vals.Where(eq => eq.b == 0).ToList(); // a { eqs .SubstituteEqLs(vals) .EliminateVariables( F1x_m1, F2x_m1, F3x_m1, F1y_m1, F2y_m1, F3y_m1, Fx_m1, Fy_m1, F1x_m2, F2x_m2, F3x_m2, F1y_m2, F2y_m2, F3y_m2, Fx_m2, Fy_m2, ax_m2, n, T, ay_m1 ) .AssertEqTo( a == (g * m1 - g * m2 * sin(incline)) / (-m1 - m2) ) .SubstituteEq(m1 == 10.0) .SubstituteEq(m2 == 5.0) .SubstituteEq(incline == 45 * Math.PI / 180) .SubstituteEq(g == 9.8) .AssertEqTo(a == -4.2234511814572784); } // T { eqs .SubstituteEqLs(vals) .EliminateVariables( F1x_m1, F2x_m1, F3x_m1, F1y_m1, F2y_m1, F3y_m1, Fx_m1, Fy_m1, F1x_m2, F2x_m2, F3x_m2, F1y_m2, F2y_m2, F3y_m2, Fx_m2, Fy_m2, ax_m2, n, a, ay_m1 ) .IsolateVariable(T) .RationalizeExpression() .AssertEqTo( T == m1 * (-g * m2 - g * m2 * sin(incline)) / (-m1 - m2) ); } } } [Fact] public void PSE_5E_E5_10_Obj3() { // Acceleration of Two Objects Connected by a Cord - Obj3 // // A ball of mass m1 and a block of mass m2 are attached by a // lightweight cord that passes over a frictionless pulley of // negligible mass, as shown in: // // http://i.imgur.com/XMHM6On.png // // The block lies on a frictionless incline of angle th. // // Find the magnitude of the acceleration of the two objects // and the tension in the cord. var bal = new Obj2("bal"); var blk = new Obj3("blk"); var th = new Symbol("th"); var T = new Symbol("T"); // tension in cable var g = new Symbol("g"); // gravity var n = new Symbol("n"); // normal force on block var a = new Symbol("a"); var m1 = new Symbol("m1"); var m2 = new Symbol("m2"); var Pi = new Symbol("Pi"); var eqs = and( blk.ax == bal.ay, // the block moves right as the ball moves up a == blk.ax, bal.Equations(), blk.Equations() ); DoubleFloat.tolerance = 0.00001; var vals = new List { bal.ax == 0, bal.m == m1, bal.F1 == T, bal.th1 == (90).ToRadians(), // force 1 is straight up bal.F2 == m1 * g, bal.th2 == (270).ToRadians(), // force 2 is straight down blk.ay == 0, blk.m == m2, blk.F1 == n, blk.th1 == (90).ToRadians(), // force 1 is straight up blk.F2 == T, blk.th2 == (180).ToRadians(), // force 2 is straight down blk.F3 == m2 * g, blk.th3 == (270).ToRadians() + th // force 3 direction }; // a eqs .SubstituteEqLs(vals) .EliminateVariables( bal.ΣFx, bal.F1x, bal.F2x, bal.ΣFy, bal.F1y, bal.F2y, blk.ΣFx, blk.F1x, blk.F2x, blk.F3x, blk.ΣFy, blk.F1y, blk.F2y, blk.F3y, blk.ax, bal.ay, T, n ) .IsolateVariable(a) .AssertEqTo( a == (g * m1 - g * m2 * sin(th)) / (-m1 - m2) ); // T eqs .SubstituteEqLs(vals) .EliminateVariables( bal.ΣFx, bal.F1x, bal.F2x, bal.ΣFy, bal.F1y, bal.F2y, blk.ΣFx, blk.F1x, blk.F2x, blk.F3x, blk.ΣFy, blk.F1y, blk.F2y, blk.F3y, blk.ax, bal.ay, a, n ) .IsolateVariable(T) .RationalizeExpression() .AssertEqTo( T == m1 * (-g * m2 - g * m2 * sin(th)) / (-m1 - m2) ); } [Fact] public void PSE_5E_E5_12() { // Experimental Determination of μs and μk // // The following is a simple method of measuring coefficients of // friction: Suppose a block is placed on a rough surface // inclined relative to the horizontal, as shown in Figure 5.19. // The incline angle is increased until the block starts to move. // Let us show that by measuring the critical angle θ_c at which this // slipping just occurs, we can obtain μs. //////////////////////////////////////////////////////////////////////////////// var F1_m1 = new Symbol("F1_m1"); // force 1 on mass 1 var F2_m1 = new Symbol("F2_m1"); // force 2 on mass 1 var F3_m1 = new Symbol("F3_m1"); // force 3 on mass 1 var th1_m1 = new Symbol("th1_m1"); // direction of force 1 on mass 1 var th2_m1 = new Symbol("th2_m1"); // direction of force 2 on mass 1 var th3_m1 = new Symbol("th3_m1"); // direction of force 3 on mass 1 var F1x_m1 = new Symbol("F1x_m1"); // x-component of force 1 on mass 1 var F2x_m1 = new Symbol("F2x_m1"); // x-component of force 2 on mass 1 var F3x_m1 = new Symbol("F3x_m1"); // x-component of force 3 on mass 1 var F1y_m1 = new Symbol("F1y_m1"); // y-component of force 1 on mass 1 var F2y_m1 = new Symbol("F2y_m1"); // y-component of force 2 on mass 1 var F3y_m1 = new Symbol("F3y_m1"); // y-component of force 3 on mass 1 var Fx_m1 = new Symbol("Fx_m1"); // x-component of total force on mass 1 var Fy_m1 = new Symbol("Fy_m1"); // y-component of total force on mass 1 var ax_m1 = new Symbol("ax_m1"); // x-component of acceleration of mass 1 var ay_m1 = new Symbol("ay_m1"); // y-component of acceleration of mass 1 var m1 = new Symbol("m1"); //////////////////////////////////////////////////////////////////////////////// var incline = new Symbol("incline"); var f_s = new Symbol("f_s"); // force due to static friction var g = new Symbol("g"); // gravity var n = new Symbol("n"); // normal force on block var a = new Symbol("a"); var Pi = new Symbol("Pi"); var mu_s = new Symbol("mu_s"); // coefficient of static friction var eqs = and( F1x_m1 == F1_m1 * cos(th1_m1), F2x_m1 == F2_m1 * cos(th2_m1), F3x_m1 == F3_m1 * cos(th3_m1), F1y_m1 == F1_m1 * sin(th1_m1), F2y_m1 == F2_m1 * sin(th2_m1), F3y_m1 == F3_m1 * sin(th3_m1), Fx_m1 == F1x_m1 + F2x_m1 + F3x_m1, Fy_m1 == F1y_m1 + F2y_m1 + F3y_m1, Fx_m1 == m1 * ax_m1, Fy_m1 == m1 * ay_m1, f_s == mu_s * n ); DoubleFloat.tolerance = 0.00001; { var vals = new List() { ax_m1 == 0, ay_m1 == 0, F1_m1 == n, F2_m1 == f_s, F3_m1 == m1 * g, th1_m1 == 90 * Pi / 180, // force 1 is straight up th2_m1 == 180 * Pi / 180, // force 2 is straight down th3_m1 == 270 * Pi / 180 + incline // force 3 direction }; var zeros = vals.Where(eq => eq.b == 0).ToList(); // mu_s { eqs .SubstituteEqLs(vals) .EliminateVariables( F1x_m1, F2x_m1, F3x_m1, F1y_m1, F2y_m1, F3y_m1, Fx_m1, Fy_m1, f_s, n ) .IsolateVariable(mu_s) .DeepSelect(SinCosToTanFunc) .AssertEqTo(mu_s == tan(incline)); } } } [Fact] public void PSE_5E_E5_13() { // The Sliding Hockey Puck // // A hockey puck on a frozen pond is given an initial speed of // 20.0 m/s. If the puck always remains on the ice and slides // 115 m before coming to rest, determine the coefficient of // kinetic friction between the puck and ice. //////////////////////////////////////////////////////////////////////////////// var s = new Symbol("s"); // displacement var u = new Symbol("u"); // initial velocity var v = new Symbol("v"); // final velocity var a = new Symbol("a"); // acceleration var t = new Symbol("t"); // time elapsed var F1_m1 = new Symbol("F1_m1"); // force 1 on mass 1 var F2_m1 = new Symbol("F2_m1"); // force 2 on mass 1 var F3_m1 = new Symbol("F3_m1"); // force 3 on mass 1 var th1_m1 = new Symbol("th1_m1"); // direction of force 1 on mass 1 var th2_m1 = new Symbol("th2_m1"); // direction of force 2 on mass 1 var th3_m1 = new Symbol("th3_m1"); // direction of force 3 on mass 1 var F1x_m1 = new Symbol("F1x_m1"); // x-component of force 1 on mass 1 var F2x_m1 = new Symbol("F2x_m1"); // x-component of force 2 on mass 1 var F3x_m1 = new Symbol("F3x_m1"); // x-component of force 3 on mass 1 var F1y_m1 = new Symbol("F1y_m1"); // y-component of force 1 on mass 1 var F2y_m1 = new Symbol("F2y_m1"); // y-component of force 2 on mass 1 var F3y_m1 = new Symbol("F3y_m1"); // y-component of force 3 on mass 1 var Fx_m1 = new Symbol("Fx_m1"); // x-component of total force on mass 1 var Fy_m1 = new Symbol("Fy_m1"); // y-component of total force on mass 1 var ax_m1 = new Symbol("ax_m1"); // x-component of acceleration of mass 1 var ay_m1 = new Symbol("ay_m1"); // y-component of acceleration of mass 1 var m1 = new Symbol("m1"); //////////////////////////////////////////////////////////////////////////////// // var incline = new Symbol("incline"); var f_s = new Symbol("f_s"); // force due to static friction var f_k = new Symbol("f_k"); // force due to kinetic friction var g = new Symbol("g"); // gravity var n = new Symbol("n"); // normal force on block // var a = new Symbol("a"); var Pi = new Symbol("Pi"); var mu_s = new Symbol("mu_s"); // coefficient of static friction var mu_k = new Symbol("mu_k"); // coefficient of kinetic friction var eqs = and( a == ax_m1, v == u + a * t, s == (u + v) * t / 2, F1x_m1 == F1_m1 * cos(th1_m1), F2x_m1 == F2_m1 * cos(th2_m1), F3x_m1 == F3_m1 * cos(th3_m1), F1y_m1 == F1_m1 * sin(th1_m1), F2y_m1 == F2_m1 * sin(th2_m1), F3y_m1 == F3_m1 * sin(th3_m1), Fx_m1 == F1x_m1 + F2x_m1 + F3x_m1, Fy_m1 == F1y_m1 + F2y_m1 + F3y_m1, Fx_m1 == m1 * ax_m1, Fy_m1 == m1 * ay_m1, f_s == mu_s * n, f_k == mu_k * n ); DoubleFloat.tolerance = 0.00001; { var symbolic_vals = new List() { F1_m1 == n, F2_m1 == f_k, F3_m1 == m1 * g, th1_m1 == 90 * Pi / 180, // force 1 is straight up th2_m1 == 180 * Pi / 180, // force 2 is left th3_m1 == 270 * Pi / 180 // force 3 is straight down }; var vals = new List() { //ax_m1 == 0, ay_m1 == 0, s == 115, u == 20, v == 0, g == 9.8 }; var zeros = vals.Where(eq => eq.b == 0).ToList(); // mu_k { eqs .SubstituteEqLs(zeros) .SubstituteEqLs(symbolic_vals) .EliminateVariables( t, F1x_m1, F2x_m1, F3x_m1, F1y_m1, F2y_m1, F3y_m1, Fx_m1, Fy_m1, f_s, f_k, n, ax_m1, a ) .IsolateVariable(mu_k) .AssertEqTo(mu_k == (u ^ 2) / s / g / 2) .SubstituteEqLs(vals) .AssertEqTo(mu_k == 0.17746228926353147); } } } [Fact] public void PSE_5E_E5_14() { // Acceleration of Two Connected Objects When Friction Is Present // // A block of mass m1 on a rough, horizontal surface is connected // to a ball of mass m2 by a lightweight cord over a lightweight, // frictionless pulley, as shown: // // http://i.imgur.com/0fHOmGJ.png // // A force of magnitude F at an angle th with the horizontal is // applied to the block as shown. The coefficient of kinetic // friction between the block and surface is mu_k. // // Determine the magnitude of the acceleration of the two objects. //////////////////////////////////////////////////////////////////////////////// var F1_m1 = new Symbol("F1_m1"); // force 1 on mass 1 var F2_m1 = new Symbol("F2_m1"); // force 2 on mass 1 var F3_m1 = new Symbol("F3_m1"); // force 3 on mass 1 var F4_m1 = new Symbol("F4_m1"); // force 4 on mass 1 var F5_m1 = new Symbol("F5_m1"); // force 5 on mass 1 var th1_m1 = new Symbol("th1_m1"); // direction of force 1 on mass 1 var th2_m1 = new Symbol("th2_m1"); // direction of force 2 on mass 1 var th3_m1 = new Symbol("th3_m1"); // direction of force 3 on mass 1 var th4_m1 = new Symbol("th4_m1"); // direction of force 4 on mass 1 var th5_m1 = new Symbol("th5_m1"); // direction of force 5 on mass 1 var F1x_m1 = new Symbol("F1x_m1"); // x-component of force 1 on mass 1 var F2x_m1 = new Symbol("F2x_m1"); // x-component of force 2 on mass 1 var F3x_m1 = new Symbol("F3x_m1"); // x-component of force 3 on mass 1 var F4x_m1 = new Symbol("F4x_m1"); // x-component of force 4 on mass 1 var F5x_m1 = new Symbol("F5x_m1"); // x-component of force 5 on mass 1 var F1y_m1 = new Symbol("F1y_m1"); // y-component of force 1 on mass 1 var F2y_m1 = new Symbol("F2y_m1"); // y-component of force 2 on mass 1 var F3y_m1 = new Symbol("F3y_m1"); // y-component of force 3 on mass 1 var F4y_m1 = new Symbol("F4y_m1"); // y-component of force 4 on mass 1 var F5y_m1 = new Symbol("F5y_m1"); // y-component of force 5 on mass 1 var Fx_m1 = new Symbol("Fx_m1"); // x-component of total force on mass 1 var Fy_m1 = new Symbol("Fy_m1"); // y-component of total force on mass 1 var ax_m1 = new Symbol("ax_m1"); // x-component of acceleration of mass 1 var ay_m1 = new Symbol("ay_m1"); // y-component of acceleration of mass 1 var m1 = new Symbol("m1"); //////////////////////////////////////////////////////////////////////////////// var F1_m2 = new Symbol("F1_m2"); // force 1 on mass 2 var F2_m2 = new Symbol("F2_m2"); // force 2 on mass 2 var th1_m2 = new Symbol("th1_m2"); // direction of force 1 on mass 2 var th2_m2 = new Symbol("th2_m2"); // direction of force 2 on mass 2 var F1x_m2 = new Symbol("F1x_m2"); // x-component of force 1 on mass 2 var F2x_m2 = new Symbol("F2x_m2"); // x-component of force 2 on mass 2 var F1y_m2 = new Symbol("F1y_m2"); // y-component of force 1 on mass 2 var F2y_m2 = new Symbol("F2y_m2"); // y-component of force 2 on mass 2 var Fx_m2 = new Symbol("Fx_m2"); // x-component of total force on mass 2 var Fy_m2 = new Symbol("Fy_m2"); // y-component of total force on mass 2 var ax_m2 = new Symbol("ax_m2"); // x-component of acceleration of mass 2 var ay_m2 = new Symbol("ay_m2"); // y-component of acceleration of mass 2 var m2 = new Symbol("m2"); //////////////////////////////////////////////////////////////////////////////// var F = new Symbol("F"); // force applied at angle on block var th = new Symbol("th"); // angle of force applied on block var T = new Symbol("T"); // tension in cable var g = new Symbol("g"); // gravity var n = new Symbol("n"); // normal force on block var a = new Symbol("a"); var Pi = new Symbol("Pi"); var f_k = new Symbol("f_k"); // force due to kinetic friction var mu_k = new Symbol("mu_k"); // coefficient of kinetic friction var eqs = and( ax_m1 == ay_m2, // the block moves right as the ball moves up //////////////////////////////////////////////////////////////////////////////// F1x_m1 == F1_m1 * cos(th1_m1), F2x_m1 == F2_m1 * cos(th2_m1), F3x_m1 == F3_m1 * cos(th3_m1), F4x_m1 == F4_m1 * cos(th4_m1), F5x_m1 == F5_m1 * cos(th5_m1), F1y_m1 == F1_m1 * sin(th1_m1), F2y_m1 == F2_m1 * sin(th2_m1), F3y_m1 == F3_m1 * sin(th3_m1), F4y_m1 == F4_m1 * sin(th4_m1), F5y_m1 == F5_m1 * sin(th5_m1), Fx_m1 == F1x_m1 + F2x_m1 + F3x_m1 + F4x_m1 + F5x_m1, Fy_m1 == F1y_m1 + F2y_m1 + F3y_m1 + F4y_m1 + F5y_m1, Fx_m1 == m1 * ax_m1, Fy_m1 == m1 * ay_m1, //////////////////////////////////////////////////////////////////////////////// F1x_m2 == F1_m2 * cos(th1_m2), F2x_m2 == F2_m2 * cos(th2_m2), F1y_m2 == F1_m2 * sin(th1_m2), F2y_m2 == F2_m2 * sin(th2_m2), Fx_m2 == F1x_m2 + F2x_m2, Fy_m2 == F1y_m2 + F2y_m2, Fx_m2 == m2 * ax_m2, Fy_m2 == m2 * ay_m2, //////////////////////////////////////////////////////////////////////////////// f_k == mu_k * n, a == ax_m1 ); DoubleFloat.tolerance = 0.00001; { var vals = new List() { ay_m1 == 0, // block moves horizontally ax_m2 == 0, // ball moves vertically F1_m1 == F, th1_m1 == th, // force applied at angle F2_m1 == n, th2_m1 == 90 * Pi / 180, // normal force is straight up F3_m1 == T, th3_m1 == 180 * Pi / 180, // force due to cord is left F4_m1 == f_k, th4_m1 == 180 * Pi / 180, // force due to friction is left F5_m1 == m1 * g, th5_m1 == 270 * Pi / 180, // force due to gravity is down F1_m2 == T, th1_m2 == 90 * Pi / 180, // force due to cord is up F2_m2 == m2 * g, th2_m2 == 270 * Pi / 180 // force due to gravity is down }; var zeros = vals.Where(eq => eq.b == 0).ToList(); // a { eqs .SubstituteEqLs(vals) .EliminateVariables( ax_m1, Fx_m1, Fy_m1, Fx_m2, Fy_m2, F1x_m1, F2x_m1, F3x_m1, F4x_m1, F5x_m1, F1y_m1, F2y_m1, F3y_m1, F4y_m1, F5y_m1, F1x_m2, F2x_m2, F1y_m2, F2y_m2, T, f_k, n, ay_m2 ) .AssertEqTo( a == (g * m2 + g * m1 * mu_k - F * mu_k * sin(th) - cos(th) * F) / (-m1 - m2) ); } } } [Fact] public void PSE_5E_E5_14_Obj5() { // Acceleration of Two Connected Objects When Friction Is Present - Obj5 // // A block of mass m1 on a rough, horizontal surface is connected // to a ball of mass m2 by a lightweight cord over a lightweight, // frictionless pulley, as shown: // // http://i.imgur.com/0fHOmGJ.png // // A force of magnitude F at an angle th with the horizontal is // applied to the block as shown. The coefficient of kinetic // friction between the block and surface is mu_k. // // Determine the magnitude of the acceleration of the two objects. var blk = new Obj5("blk"); var bal = new Obj3("bal"); var F = new Symbol("F"); // force applied at angle on block var th = new Symbol("th"); // angle of force applied on block var T = new Symbol("T"); // tension in cable var g = new Symbol("g"); // gravity var n = new Symbol("n"); // normal force on block var a = new Symbol("a"); var Pi = new Symbol("Pi"); var f_k = new Symbol("f_k"); // force due to kinetic friction var mu_k = new Symbol("mu_k"); // coefficient of kinetic friction var m1 = new Symbol("m1"); var m2 = new Symbol("m2"); var eqs = and( blk.ax == bal.ay, // the block moves right as the ball moves up blk.Equations(), bal.Equations(), f_k == mu_k * n, a == blk.ax ); var vals = new List() { blk.ay == 0, // block moves horizontally blk.F1 == F, blk.th1 == th, // block moves horizontally blk.F2 == n, blk.th2 == 90 * Pi / 180, // normal force is straight up blk.F3 == T, blk.th3 == 180 * Pi / 180, // force due to cord is left blk.F4 == f_k, blk.th4 == 180 * Pi / 180, // force due to friction is left blk.F5 == blk.m * g, blk.th5 == 270 * Pi / 180, // force due to gravity is down bal.ax == 0, // ball moves vertically bal.F1 == T, bal.th1 == 90 * Pi / 180, // force due to cord is up bal.F2 == bal.m * g, bal.th2 == 270 * Pi / 180, // force due to gravity is down bal.F3 == 0, blk.m == m1, bal.m == m2 }; // a eqs .SubstituteEqLs(vals) .EliminateVariables( blk.ax, blk.ΣFx, blk.ΣFy, bal.ΣFx, bal.ΣFy, blk.F1x, blk.F2x, blk.F3x, blk.F4x, blk.F5x, blk.F1y, blk.F2y, blk.F3y, blk.F4y, blk.F5y, bal.F1x, bal.F2x, bal.F3x, bal.F1y, bal.F2y, bal.F3y, T, f_k, n, bal.ay ) .AssertEqTo( a == (g * m2 + g * m1 * mu_k - F * mu_k * sin(th) - cos(th) * F) / (-m1 - m2) ); } [Fact] public void PSE_5E_P5_25() { // A bag of cement of weight F_g hangs from three wires as // shown in http://i.imgur.com/f5JpLjY.png // // Two of the wires make angles th1 and th2 with the horizontal. // If the system is in equilibrium, show that the tension in the // left -hand wire is: // // T1 == F_g cos(th2) / sin(th1 + th2) //////////////////////////////////////////////////////////////////////////////// var F1_m1 = new Symbol("F1_m1"); // force 1 on mass 1 var F2_m1 = new Symbol("F2_m1"); // force 2 on mass 1 var F3_m1 = new Symbol("F3_m1"); // force 3 on mass 1 var th1_m1 = new Symbol("th1_m1"); // direction of force 1 on mass 1 var th2_m1 = new Symbol("th2_m1"); // direction of force 2 on mass 1 var th3_m1 = new Symbol("th3_m1"); // direction of force 3 on mass 1 var F1x_m1 = new Symbol("F1x_m1"); // x-component of force 1 on mass 1 var F2x_m1 = new Symbol("F2x_m1"); // x-component of force 2 on mass 1 var F3x_m1 = new Symbol("F3x_m1"); // x-component of force 3 on mass 1 var F1y_m1 = new Symbol("F1y_m1"); // y-component of force 1 on mass 1 var F2y_m1 = new Symbol("F2y_m1"); // y-component of force 2 on mass 1 var F3y_m1 = new Symbol("F3y_m1"); // y-component of force 3 on mass 1 var Fx_m1 = new Symbol("Fx_m1"); // x-component of total force on mass 1 var Fy_m1 = new Symbol("Fy_m1"); // y-component of total force on mass 1 var ax_m1 = new Symbol("ax_m1"); // x-component of acceleration of mass 1 var ay_m1 = new Symbol("ay_m1"); // y-component of acceleration of mass 1 var m1 = new Symbol("m1"); //////////////////////////////////////////////////////////////////////////////// var g = new Symbol("g"); // gravity var a = new Symbol("a"); var Pi = new Symbol("Pi"); var T1 = new Symbol("T1"); var T2 = new Symbol("T2"); var T3 = new Symbol("T3"); var th1 = new Symbol("th1"); var th2 = new Symbol("th2"); var eqs = and( F1x_m1 == F1_m1 * cos(th1_m1), F2x_m1 == F2_m1 * cos(th2_m1), F3x_m1 == F3_m1 * cos(th3_m1), F1y_m1 == F1_m1 * sin(th1_m1), F2y_m1 == F2_m1 * sin(th2_m1), F3y_m1 == F3_m1 * sin(th3_m1), Fx_m1 == F1x_m1 + F2x_m1 + F3x_m1, Fy_m1 == F1y_m1 + F2y_m1 + F3y_m1, Fx_m1 == m1 * ax_m1, Fy_m1 == m1 * ay_m1 ); DoubleFloat.tolerance = 0.00001; { var vals = new List() { ax_m1 == 0, ay_m1 == 0, F1_m1 == T2, F2_m1 == T1, F3_m1 == m1 * g, th1_m1 == th2, th2_m1 == 180 * Pi / 180 - th1, th3_m1 == 270 * Pi / 180 }; var zeros = vals.Where(eq => eq.b == 0).ToList(); // T1 { eqs .SubstituteEqLs(vals) .EliminateVariables( F1x_m1, F2x_m1, F3x_m1, F1y_m1, F2y_m1, F3y_m1, Fx_m1, Fy_m1, T2 ) .IsolateVariable(T1) .RationalizeExpression() .DeepSelect(SumDifferenceFormulaAFunc) .AssertEqTo( T1 == cos(th2) * g * m1 / sin(th1 + th2) ); } } } [Fact] public void PSE_5E_P5_25_Obj() { // A bag of cement of weight F_g hangs from three wires as // shown in http://i.imgur.com/f5JpLjY.png // // Two of the wires make angles th1 and th2 with the horizontal. // If the system is in equilibrium, show that the tension in the // left -hand wire is: // // T1 == F_g cos(th2) / sin(th1 + th2) var bag = new Obj3("bag"); var T1 = new Symbol("T1"); var T2 = new Symbol("T2"); var T3 = new Symbol("T3"); var F_g = new Symbol("F_g"); var th1 = new Symbol("th1"); var th2 = new Symbol("th2"); var eqs = bag.Equations(); var vals = new List() { bag.ax == 0, bag.ay == 0, bag.F1 == T1, bag.th1 == (180).ToRadians() - th1, bag.F2 == T2, bag.th2 == th2, bag.F3 == F_g, bag.th3 == (270).ToRadians() }; eqs .SubstituteEqLs(vals) .EliminateVariables( bag.ΣFx, bag.F1x, bag.F2x, bag.F3x, bag.ΣFy, bag.F1y, bag.F2y, bag.F3y, T2 ) .IsolateVariable(T1) .RationalizeExpression() .DeepSelect(SumDifferenceFormulaAFunc) .AssertEqTo(T1 == cos(th2) * F_g / sin(th1 + th2)); } [Fact] public void PSE_5E_P5_31() { // Two people pull as hard as they can on ropes attached // to a boat that has a mass of 200 kg. If they pull in the // same direction, the boat has an acceleration of // 1.52 m/s^2 to the right. If they pull in opposite directions, // the boat has an acceleration of 0.518 m/s^2 to the // left. // // What is the force exerted by each person on the // boat? (Disregard any other forces on the boat.) //////////////////////////////////////////////////////////////////////////////// var F1_m1 = new Symbol("F1_m1"); // force 1 on mass 1 var F2_m1 = new Symbol("F2_m1"); // force 2 on mass 1 var th1_m1 = new Symbol("th1_m1"); // direction of force 1 on mass 1 var th2_m1 = new Symbol("th2_m1"); // direction of force 2 on mass 1 var F1x_m1 = new Symbol("F1x_m1"); // x-component of force 1 on mass 1 var F2x_m1 = new Symbol("F2x_m1"); // x-component of force 2 on mass 1 var F1y_m1 = new Symbol("F1y_m1"); // y-component of force 1 on mass 1 var F2y_m1 = new Symbol("F2y_m1"); // y-component of force 2 on mass 1 var Fx_m1 = new Symbol("Fx_m1"); // x-component of total force on mass 1 var Fy_m1 = new Symbol("Fy_m1"); // y-component of total force on mass 1 var ax_m1 = new Symbol("ax_m1"); // x-component of acceleration of mass 1 var ay_m1 = new Symbol("ay_m1"); // y-component of acceleration of mass 1 var m1 = new Symbol("m1"); //////////////////////////////////////////////////////////////////////////////// var F1_m2 = new Symbol("F1_m2"); // force 1 on mass 2 var F2_m2 = new Symbol("F2_m2"); // force 2 on mass 2 var th1_m2 = new Symbol("th1_m2"); // direction of force 1 on mass 2 var th2_m2 = new Symbol("th2_m2"); // direction of force 2 on mass 2 var F1x_m2 = new Symbol("F1x_m2"); // x-component of force 1 on mass 2 var F2x_m2 = new Symbol("F2x_m2"); // x-component of force 2 on mass 2 var F1y_m2 = new Symbol("F1y_m2"); // y-component of force 1 on mass 2 var F2y_m2 = new Symbol("F2y_m2"); // y-component of force 2 on mass 2 var Fx_m2 = new Symbol("Fx_m2"); // x-component of total force on mass 2 var Fy_m2 = new Symbol("Fy_m2"); // y-component of total force on mass 2 var ax_m2 = new Symbol("ax_m2"); // x-component of acceleration of mass 2 var ay_m2 = new Symbol("ay_m2"); // y-component of acceleration of mass 2 var m2 = new Symbol("m2"); //////////////////////////////////////////////////////////////////////////////// var Pi = new Symbol("Pi"); var T1 = new Symbol("T1"); var T2 = new Symbol("T2"); var eqs = and( m1 == m2, F1x_m1 == F1_m1 * cos(th1_m1), F2x_m1 == F2_m1 * cos(th2_m1), F1y_m1 == F1_m1 * sin(th1_m1), F2y_m1 == F2_m1 * sin(th2_m1), Fx_m1 == F1x_m1 + F2x_m1, Fy_m1 == F1y_m1 + F2y_m1, Fx_m1 == m1 * ax_m1, Fy_m1 == m1 * ay_m1, F1x_m2 == F1_m2 * cos(th1_m2), F2x_m2 == F2_m2 * cos(th2_m2), F1y_m2 == F1_m2 * sin(th1_m2), F2y_m2 == F2_m2 * sin(th2_m2), Fx_m2 == F1x_m2 + F2x_m2, Fy_m2 == F1y_m2 + F2y_m2, Fx_m2 == m2 * ax_m2, Fy_m2 == m2 * ay_m2 ); DoubleFloat.tolerance = 0.00001; { var vals = new List() { ay_m1 == 0, F1_m1 == T1, th1_m1 == 0, F2_m1 == T2, th2_m1 == 0, ay_m2 == 0, F1_m2 == T1, th1_m2 == 180 * Pi / 180, F2_m2 == T2, th2_m2 == 0 }; var zeros = vals.Where(eq => eq.b == 0).ToList(); var numerical_vals = new List() { m1 == 200, ax_m1 == 1.52, ax_m2 == -0.518 }; // T1 { eqs .SubstituteEqLs(vals) .EliminateVariables( m2, F1x_m1, F2x_m1, F1y_m1, F2y_m1, F1x_m2, F2x_m2, F1y_m2, F2y_m2, Fx_m1, Fy_m1, Fx_m2, Fy_m2, T2 ) .IsolateVariable(T1) .AssertEqTo( T1 == -(ax_m2 * m1 - ax_m1 * m1) / 2 ) .SubstituteEqLs(numerical_vals) .AssertEqTo(T1 == 203.8); } // T2 { eqs .SubstituteEqLs(vals) .EliminateVariables( m2, F1x_m1, F2x_m1, F1y_m1, F2y_m1, F1x_m2, F2x_m2, F1y_m2, F2y_m2, Fx_m1, Fy_m1, Fx_m2, Fy_m2, T1 ) .IsolateVariable(T2) .AssertEqTo( T2 == (ax_m1 * m1 + ax_m2 * m1) / 2 ) .SubstituteEqLs(numerical_vals) .AssertEqTo(T2 == 100.19999999999999); } } } [Fact] public void PSE_5E_P5_31_Obj() { // Two people pull as hard as they can on ropes attached // to a boat that has a mass of 200 kg. If they pull in the // same direction, the boat has an acceleration of // 1.52 m/s^2 to the right. If they pull in opposite directions, // the boat has an acceleration of 0.518 m/s^2 to the // left. // // What is the force exerted by each person on the // boat? (Disregard any other forces on the boat.) //////////////////////////////////////////////////////////////////////////////// var b1 = new Obj2("b1"); // boat in scenario 1 (same direction) var b2 = new Obj2("b2"); // boat in scenario 2 (opposite directions) var m = new Symbol("m"); //////////////////////////////////////////////////////////////////////////////// var Pi = new Symbol("Pi"); var T1 = new Symbol("T1"); var T2 = new Symbol("T2"); var eqs = and( b1.Equations(), b2.Equations() ); DoubleFloat.tolerance = 0.00001; var vals = new List() { b1.m == m, b1.ay == 0, b1.F1 == T1, b1.th1 == 0, b1.F2 == T2, b1.th2 == 0, b2.m == m, b2.ay == 0, b2.F1 == T1, b2.th1 == (180).ToRadians(), b2.F2 == T2, b2.th2 == 0 }; var zeros = vals.Where(eq => eq.b == 0).ToList(); var numerical_vals = new List() { m == 200, b1.ax == 1.52, b2.ax == -0.518 }; // T1 eqs .SubstituteEqLs(vals) .EliminateVariables( b1.ΣFx, b1.F1x, b1.F2x, b1.ΣFy, b1.F1y, b1.F2y, b2.ΣFx, b2.F1x, b2.F2x, b2.ΣFy, b2.F1y, b2.F2y, T2 ) .IsolateVariable(T1) .AssertEqTo( T1 == -(b2.ax * m - b1.ax * m) / 2 ) .SubstituteEqLs(numerical_vals) .AssertEqTo(T1 == 203.8); // T2 eqs .SubstituteEqLs(vals) .EliminateVariables( b1.ΣFx, b1.F1x, b1.F2x, b1.ΣFy, b1.F1y, b1.F2y, b2.ΣFx, b2.F1x, b2.F2x, b2.ΣFy, b2.F1y, b2.F2y, T1 ) .IsolateVariable(T2) .AssertEqTo( T2 == (b1.ax * m + b2.ax * m) / 2 ) .SubstituteEqLs(numerical_vals) .AssertEqTo(T2 == 100.19999999999999); } [Fact] public void PSE_5E_P5_55() { // An inventive child named Pat wants to reach an apple // in a tree without climbing the tree. Sitting in a chair // connected to a rope that passes over a frictionless pulley // Pat pulls on the loose end of the rope with such a force // that the spring scale reads 250 N. Pat’s weight is 320 N, // and the chair weighs 160 N. // // http://i.imgur.com/wwlypzB.png // // (a) Draw free - body diagrams for Pat and the chair considered as // separate systems, and draw another diagram for Pat and // the chair considered as one system. // // (b) Show that the acceleration of the system is upward and // find its magnitude. // // (c) Find the force Pat exerts on the chair. var b = new Obj3("b"); // boy var c = new Obj3("c"); // chair var s = new Obj3("s"); // system var T = new Symbol("T"); // rope tension var n = new Symbol("n"); // normal force var Fg_b = new Symbol("Fg_b"); // force due to gravity of the boy var Fg_c = new Symbol("Fg_c"); // force due to gravity of the chair var Fg_s = new Symbol("Fg_s"); // force due to gravity of the system var a = new Symbol("a"); // acceleration var Pi = new Symbol("Pi"); var g = new Symbol("g"); var eqs = and( Fg_b == b.m * g, Fg_c == c.m * g, Fg_s == s.m * g, Fg_s == Fg_c + Fg_b, s.Equations(), c.Equations() ); var vals = new List() { //b.ax == 0, c.ax == 0, s.ax == 0, //b.F1 == T, b.th1 == 90 * Pi / 180, //b.F2 == n, b.th2 == 90 * Pi / 180, //b.F3 == b.m * g, b.th3 == 270 * Pi / 180, c.F1 == T, c.th1 == 90 * Pi / 180, c.F2 == n, c.th2 == 270 * Pi / 180, c.F3 == Fg_c, c.th3 == 270 * Pi / 180, s.F1 == T, s.th1 == 90 * Pi / 180, s.F2 == T, s.th2 == 90 * Pi / 180, s.F3 == Fg_s, s.th3 == 270 * Pi / 180, //b.ay == a, c.ay == a, s.ay == a }; var numerical_vals = new List() { T == 250.0, Fg_b == 320, Fg_c == 160, g == 9.8 }; DoubleFloat.tolerance = 0.00001; // a eqs .SubstituteEqLs(vals) .EliminateVariables( s.ΣFx, s.F1x, s.F2x, s.F3x, s.ΣFy, s.F1y, s.F2y, s.F3y, c.ΣFx, c.F1x, c.F2x, c.F3x, c.ΣFy, c.F1y, c.F2y, c.F3y, n, s.m, Fg_s, b.m, c.m ) .IsolateVariable(a) .AssertEqTo( a == -g * (Fg_b + Fg_c - 2 * T) / (Fg_b + Fg_c) ) .SubstituteEqLs(numerical_vals) .AssertEqTo(a == 0.40833333333333333); // n eqs .SubstituteEqLs(vals) .EliminateVariables( s.ΣFx, s.F1x, s.F2x, s.F3x, s.ΣFy, s.F1y, s.F2y, s.F3y, c.ΣFx, c.F1x, c.F2x, c.F3x, c.ΣFy, c.F1y, c.F2y, c.F3y, c.m, s.m, Fg_s, b.m, a ) .IsolateVariable(n) .AssertEqTo( n == -1 * (Fg_c - T - Fg_c * (Fg_b + Fg_c - 2 * T) / (Fg_b + Fg_c)) ) .SubstituteEqLs(numerical_vals); DoubleFloat.tolerance = null; } [Fact] public void PSE_5E_P5_59() { // A mass M is held in place by an applied force F and a // pulley system: // // http://i.imgur.com/TPAHTlW.png // // The pulleys are massless and frictionless. Find // // (a) the tension in each section of rope, T1, T2, T3, T4, and T5 // // (b) the magnitude of F. // // (Hint: Draw a free - body diagram for each pulley.) var pul1_F = new Symbol("pul1_F"); // magnitude of total force on pully 1 var pul1_m = new Symbol("pul1_m"); // mass of pully 1 var pul1_a = new Symbol("pul1_a"); // acceleration of pully 1 var pul2_F = new Symbol("pul2_F"); // magnitude of total force on pully 2 var pul2_m = new Symbol("pul2_m"); // mass of pully 2 var pul2_a = new Symbol("pul2_a"); // acceleration of pully 2 var T1 = new Symbol("T1"); var T2 = new Symbol("T2"); var T3 = new Symbol("T3"); var T4 = new Symbol("T4"); var T5 = new Symbol("T5"); var F = new Symbol("F"); var M = new Symbol("M"); var g = new Symbol("g"); var eqs = and( T1 == F, T2 == T3, T1 == T3, T5 == M * g, pul1_a == 0, pul1_m == 0, pul1_F == T4 - T1 - T2 - T3, pul1_F == pul1_m * pul1_a, pul2_m == 0, pul2_F == T2 + T3 - T5, pul2_F == pul2_m * pul2_a ); DoubleFloat.tolerance = 0.00001; // T1 { eqs .EliminateVariables(pul1_F, pul2_F, pul1_m, pul2_m, pul1_a, T2, T3, T4, T5, F) .IsolateVariable(T1) .AssertEqTo(T1 == g * M / 2); } // T2 { eqs .EliminateVariables(pul1_F, pul2_F, pul1_m, pul2_m, pul1_a, T1, T3, T4, T5, F) .IsolateVariable(T2) .AssertEqTo(T2 == g * M / 2); } // T3 { eqs .EliminateVariables(pul1_F, pul2_F, pul1_m, pul2_m, pul1_a, T1, T2, T4, T5, F) .IsolateVariable(T3) .AssertEqTo(T3 == g * M / 2); } // T4 { eqs .EliminateVariables(pul1_F, pul2_F, pul1_m, pul2_m, pul1_a, T1, T2, T3, T5, F) .IsolateVariable(T4) .AssertEqTo(T4 == g * M * 3 / 2); } // T5 { eqs .EliminateVariables(pul1_F, pul2_F, pul1_m, pul2_m, pul1_a, T1, T2, T3, T4, F) .AssertEqTo(T5 == g * M); } // F { eqs .EliminateVariables(pul1_F, pul2_F, pul1_m, pul2_m, pul1_a, T1, T2, T3, T4, T5) .IsolateVariable(F) .AssertEqTo(F == g * M / 2); } } [Fact] public void PSE_5E_P5_69() { // What horizontal force must be applied to the cart shown: // http://i.imgur.com/fpkzsYI.png // so that the blocks remain stationary relative to the cart? // Assume all surfaces, wheels, and pulley are frictionless. // (Hint:Note that the force exerted by the string accelerates m1.) var blk1 = new Obj3("blk1"); var blk2 = new Obj3("blk2"); var sys = new Obj3("sys"); var m1 = new Symbol("m1"); var m2 = new Symbol("m2"); var T = new Symbol("T"); var F = new Symbol("F"); var M = new Symbol("M"); var g = new Symbol("g"); var a = new Symbol("a"); var eqs = and( blk1.Equations(), blk2.Equations(), sys.Equations() ); var vals = new List() { blk1.ax == a, blk1.ay == 0, blk1.m == m1, blk1.F1 == T, blk1.th1 == 0, blk1.th2 == (90).ToRadians(), blk1.th3 == (270).ToRadians(), blk2.ax == a, blk2.ay == 0, blk2.m == m2, blk2.th1 == 0, blk2.F2 == T, blk2.th2 == (90).ToRadians(), blk2.F3 == m2 * g, blk2.th3 == (270).ToRadians(), sys.ax == a, sys.ay == 0, sys.m == M + m1 + m2, sys.F1 == F, sys.th1 == 0, sys.th2 == (90).ToRadians(), sys.th3 == (270).ToRadians() }; eqs .SubstituteEqLs(vals) .EliminateVariables( blk1.ΣFx, blk1.F1x, blk1.F2x, blk1.F3x, blk1.ΣFy, blk1.F1y, blk1.F2y, blk1.F3y, blk1.F2, blk2.ΣFx, blk2.F1x, blk2.F2x, blk2.F3x, blk2.ΣFy, blk2.F1y, blk2.F2y, blk2.F3y, blk2.F1, sys.ΣFx, sys.F1x, sys.F2x, sys.F3x, sys.ΣFy, sys.F1y, sys.F2y, sys.F3y, sys.F2, T, a ) .AssertEqTo(F == g * m2 / m1 * (M + m1 + m2)); } [Fact] public void PSE_5E_E7_7() { // A 6.0-kg block initially at rest is pulled to the right along a // horizontal, frictionless surface by a constant horizontal force // of 12 N. Find the speed of the block after it has moved 3.0 m. var W = new Symbol("W"); var F = new Symbol("F"); var d = new Symbol("d"); var Kf = new Symbol("Kf"); var Ki = new Symbol("Ki"); var m = new Symbol("m"); var vf = new Symbol("vf"); var vi = new Symbol("vi"); var eqs = and( W == F * d, W == Kf - Ki, Kf == m * (vf ^ 2) / 2, Ki == m * (vi ^ 2) / 2, m != 0 ); var vals = new List() { m == 6.0, vi == 0, F == 12, d == 3 }; // vf eqs .EliminateVariables(Kf, Ki, W) .IsolateVariable(vf) .LogicalExpand().CheckVariable(m).SimplifyEquation().SimplifyLogical() .AssertEqTo( or( and( vf == sqrt(-2 * m * (-d * F - m * (vi ^ 2) / 2)) / m, m != 0), and( vf == -sqrt(-2 * m * (-d * F - m * (vi ^ 2) / 2)) / m, m != 0))) .SubstituteEq(vi == 0) .AssertEqTo( or( and( vf == sqrt(2 * d * F * m) / m, m != 0), and( vf == -sqrt(2 * d * F * m) / m, m != 0))) .SubstituteEqLs(vals) .AssertEqTo( or( vf == 3.4641016151377544, vf == -3.4641016151377544)); } [Fact] public void PSE_5E_E7_8() { // Find the final speed of the block described in Example 7.7 if // the surface is not frictionless but instead has a coefficient of // kinetic friction of 0.15. var W = new Symbol("W"); var F = new Symbol("F"); var d = new Symbol("d"); var n = new Symbol("n"); var g = new Symbol("g"); var Kf = new Symbol("Kf"); var Ki = new Symbol("Ki"); var m = new Symbol("m"); var vf = new Symbol("vf"); var vi = new Symbol("vi"); var fk = new Symbol("fk"); var μk = new Symbol("μk"); var eqs = and( Kf == m * (vf ^ 2) / 2, Ki == m * (vi ^ 2) / 2, W == F * d, n == m * g, fk == n * μk, W - fk * d == Kf - Ki, m != 0 ); var vals = new List() { vi == 0, F == 12.0, d == 3.0, m == 6.0, μk == 0.15, g == 9.8, }; // vf eqs .EliminateVariables(Kf, Ki, W, n, fk) .IsolateVariable(vf) .LogicalExpand().SimplifyEquation().SimplifyLogical().CheckVariable(m) .SubstituteEq(vi == 0) .AssertEqTo( or( and( vf == -sqrt(2 * m * (d * F - d * g * m * μk)) / m, m != 0), and( vf == sqrt(2 * m * (d * F - d * g * m * μk)) / m, m != 0))) .SubstituteEqLs(vals) .AssertEqTo(or(vf == -1.7832554500127007, vf == 1.7832554500127007)); } [Fact] public void PSE_5E_E7_11() { // A block of mass 1.6 kg is attached to a horizontal spring that // has a force constant of 1.0 x 10^3 N/m, as shown in Figure // 7.10. The spring is compressed 2.0 cm and is then released // from rest. // (a) Calculate the speed of the block as it passes // through the equilibrium position x = 0 if the surface is frictionless. // (b) Calculate the speed of the block as it passes through // the equilibrium position if a constant frictional force of 4.0 N // retards its motion from the moment it is released. var ΣW = new Symbol("ΣW"); var Kf = new Symbol("Kf"); var Ki = new Symbol("Ki"); var m = new Symbol("m"); var d = new Symbol("d"); var k = new Symbol("k"); var vf = new Symbol("vf"); var vi = new Symbol("vi"); var fk = new Symbol("fk"); var W_s = new Symbol("W_s"); var W_f = new Symbol("W_f"); var x_max = new Symbol("x_max"); var eqs = and( W_s == k * (x_max ^ 2) / 2, Kf == m * (vf ^ 2) / 2, Ki == m * (vi ^ 2) / 2, W_f == -fk * d, ΣW == Kf - Ki, ΣW == W_s + W_f, m != 0 ); // vf { var vals = new List() { m == 1.6, vi == 0, fk == 0, k == 1000, x_max == -0.02 }; eqs .EliminateVariables(ΣW, Kf, Ki, W_f, W_s) .IsolateVariable(vf) .LogicalExpand().SimplifyEquation().SimplifyLogical().CheckVariable(m) .AssertEqTo( or( and( vf == sqrt(-2 * m * (d * fk - m * (vi ^ 2) / 2 - k * (x_max ^ 2) / 2)) / m, m != 0), and( vf == -sqrt(-2 * m * (d * fk - m * (vi ^ 2) / 2 - k * (x_max ^ 2) / 2)) / m, m != 0))) .SubstituteEqLs(vals) .AssertEqTo(or(vf == 0.5, vf == -0.5)); } // vf { var vals = new List() { m == 1.6, vi == 0, fk == 4, k == 1000, x_max == -0.02, d == 0.02 }; eqs .EliminateVariables(ΣW, Kf, Ki, W_f, W_s) .IsolateVariable(vf) .LogicalExpand().SimplifyEquation().SimplifyLogical().CheckVariable(m) .SubstituteEqLs(vals) .AssertEqTo(or(vf == 0.3872983346207417, vf == -0.3872983346207417)); } } [Fact] public void PSE_6E_P7_3() { // Batman, whose mass is 80.0kg, is dangling on the free end // of a 12.0m rope, the other end of which is fixed to a tree // limb above. He is able to get the rope in motion as only // Batman knows how, eventually getting it to swing enough // that he can reach a ledge when the rope makes a 60.0° // angle with the vertical. How much work was done by the // gravitational force on Batman in this maneuver? var m = new Symbol("m"); var a = new Symbol("a"); var W = new Symbol("W"); var F = new Symbol("F"); var d = new Symbol("d"); var yA = new Symbol("yA"); var yB = new Symbol("yB"); var th = new Symbol("th"); var len = new Symbol("len"); var eqs = and( yA == -len, yB == -len * cos(th), d == yB - yA, F == m * a, W == F * d ); var vals = new List() { m == 80, len == 12, th == (60).ToRadians(), a == -9.8 }; eqs .EliminateVariables(F, d, yA, yB) .AssertEqTo(W == a * (len - cos(th) * len) * m) .SubstituteEqLs(vals) .AssertEqTo(W == -4704.0); } [Fact] public void PSE_5E_P7_23() { // If it takes 4.00J of work to stretch a Hooke’s-law spring // 10.0cm from its unstressed length, determine the extra // work required to stretch it an additional 10.0cm. var WsAB = new Symbol("WsAB"); var WsA = new Symbol("WsA"); var WsB = new Symbol("WsB"); var k = new Symbol("k"); var xA = new Symbol("xA"); var xB = new Symbol("xB"); var eqs = and( WsA == k * (xA ^ 2) / 2, WsB == k * (xB ^ 2) / 2, WsAB == WsB - WsA ); var vals = new List() { xA == 0.1, xB == 0.2, WsA == 4 }; eqs .EliminateVariables(WsB, k) .AssertEqTo( WsAB == WsA * (xB ^ 2) / (xA ^ 2) - WsA ) .SubstituteEqLs(vals) .AssertEqTo(WsAB == 12.0); } [Fact] public void PSE_5E_P7_33() { // A 40.0-kg box initially at rest is pushed 5.00m along a // rough, horizontal floor with a constant applied horizontal // force of 130 N. If the coefficient of friction between // the box and the floor is 0.300, find // (a) the work done by the applied force // (b) the energy loss due to friction // (c) the work done by the normal force // (d) the work done by gravity // (e) the change in kinetic energy of the box // (f) the final speed of the box var ΣW = new Symbol("ΣW"); var Kf = new Symbol("Kf"); var Ki = new Symbol("Ki"); var F = new Symbol("F"); var m = new Symbol("m"); var d = new Symbol("d"); var n = new Symbol("n"); var g = new Symbol("g"); var vf = new Symbol("vf"); var vi = new Symbol("vi"); var fk = new Symbol("fk"); var W_F = new Symbol("W_F"); var W_f = new Symbol("W_f"); var μk = new Symbol("μk"); var eqs = and( n == m * g, fk == μk * n, Kf == m * (vf ^ 2) / 2, Ki == m * (vi ^ 2) / 2, W_F == F * d, W_f == -fk * d, ΣW == Kf - Ki, ΣW == W_F + W_f, m != 0 ); var vals = new List() { m == 40, vi == 0, d == 5, F == 130, μk == 0.3, g == 9.8 }; // W_F, W_f eqs .EliminateVariables(fk, n, Kf, Ki, ΣW, vf) .LogicalExpand().SimplifyEquation().SimplifyLogical().CheckVariable(m) .AssertEqTo( and( m != 0, W_F == d * F, W_f == -d * g * m * μk)) .SubstituteEqLs(vals) .AssertEqTo(and(W_F == 650, W_f == -588.0)); // ΣW eqs .EliminateVariables(W_F, W_f, fk, n, Ki, Kf) .AssertEqTo( and( ΣW == m * (vf ^ 2) / 2 - m * (vi ^ 2) / 2, ΣW == d * F - d * g * m * μk, m != 0)) .SubstituteEqLs(vals) .AssertEqTo(and(ΣW == 20 * (vf ^ 2), ΣW == 62.0)); // vf eqs .EliminateVariables(Kf, Ki, ΣW, W_F, W_f, fk, n) .IsolateVariable(vf) .LogicalExpand().SimplifyEquation().SimplifyLogical().CheckVariable(m) .AssertEqTo( or( and( vf == sqrt(-2 * m * (-d * F - m * (vi ^ 2) / 2 + d * g * m * μk)) / m, m != 0), and( vf == -sqrt(-2 * m * (-d * F - m * (vi ^ 2) / 2 + d * g * m * μk)) / m, m != 0))) .SubstituteEqLs(vals) .AssertEqTo(or(vf == 1.7606816861659009, vf == -1.7606816861659009)); } [Fact] public void PSE_5E_P7_35() { // A crate of mass 10.0kg is pulled up a rough incline with // an initial speed of 1.50 m/s.The pulling force is 100 N // parallel to the incline, which makes an angle of 20.0° // with the horizontal. The coefficient of kinetic friction is // 0.400, and the crate is pulled 5.00 m. // (a) How much work is done by gravity? // (b) How much energy is lost because of friction? // (c) How much work is done by the 100-N force? // (d) What is the change in kinetic energy of the crate? // (e) What is the speed of the crate after it has been pulled 5.00 m? var ΣW = new Symbol("ΣW"); var Kf = new Symbol("Kf"); var Ki = new Symbol("Ki"); var F = new Symbol("F"); var m = new Symbol("m"); var d = new Symbol("d"); var n = new Symbol("n"); var g = new Symbol("g"); var vf = new Symbol("vf"); var vi = new Symbol("vi"); var fk = new Symbol("fk"); var W_F = new Symbol("W_F"); var W_f = new Symbol("W_f"); var W_g = new Symbol("W_g"); var μk = new Symbol("μk"); var th = new Symbol("th"); var F_g = new Symbol("F_g"); var Pi = new Symbol("Pi"); var eqs = and( F_g == m * g, n == F_g * cos(th), fk == μk * n, Kf == m * (vf ^ 2) / 2, Ki == m * (vi ^ 2) / 2, W_F == F * d, W_f == -fk * d, W_g == -F_g * sin(th) * d, ΣW == Kf - Ki, ΣW == W_F + W_f + W_g, m != 0 ); var vals = new List() { m == 10.0, g == 9.8, d == 5.0, th == (20).ToRadians(), μk == 0.4, F == 100.0, vi == 1.5, Pi == Math.PI }; // W_g, W_f, W_F eqs .EliminateVariables(F_g, fk, n) .AssertEqTo( and( Kf == m * (vf ^ 2) / 2, Ki == m * (vi ^ 2) / 2, W_F == d * F, W_f == -cos(th) * d * g * m * μk, W_g == -d * g * m * sin(th), ΣW == Kf - Ki, ΣW == W_f + W_F + W_g, m != 0 )) .SubstituteEqLs(vals) .AssertEqTo( and( Kf == 5.0 * (vf ^ 2), Ki == 11.25, W_F == 500.0, W_f == -184.17975367403804, W_g == -167.58987022957766, ΣW == Kf - Ki, ΣW == W_f + W_F + W_g )); // ΣW eqs .EliminateVariables(F_g, fk, n, W_F, W_f, W_g) .SubstituteEqLs(vals) .AssertEqTo( and( Kf == 5.0 * (vf ^ 2), Ki == 11.25, ΣW == Kf - Ki, ΣW == 148.23037609638431 )); // vf eqs .EliminateVariables(F_g, fk, n, W_F, W_f, W_g, ΣW, Kf, Ki) .IsolateVariable(vf) .LogicalExpand().SimplifyEquation().SimplifyLogical().CheckVariable(m) .SubstituteEqLs(vals) .AssertEqTo(or(vf == 5.6476610396939435, vf == -5.6476610396939435)); } [Fact] public void PSE_5E_P7_39() { // A bullet with a mass of 5.00 g and a speed of 600 m/s // penetrates a tree to a depth of 4.00 cm. // (a) Use work and energy considerations to find the average // frictional force that stops the bullet. // (b) Assuming that the frictional force is constant, // determine how much time elapsed between the moment // the bullet entered the tree and the moment it stopped. var ΣW = new Symbol("ΣW"); var Kf = new Symbol("Kf"); var Ki = new Symbol("Ki"); var m = new Symbol("m"); var d = new Symbol("d"); var vf = new Symbol("vf"); var vi = new Symbol("vi"); var fk = new Symbol("fk"); var W_f = new Symbol("W_f"); var t = new Symbol("t"); var eqs = and( Kf == m * (vf ^ 2) / 2, Ki == m * (vi ^ 2) / 2, W_f == -fk * d, ΣW == Kf - Ki, ΣW == W_f ); var vals = new List() { m == 0.005, vi == 600.0, vf == 0.0, d == 0.04 }; // fk eqs .EliminateVariables(W_f, ΣW, Ki, Kf) .IsolateVariable(fk) .AssertEqTo( fk == (m * (vi ^ 2) / 2 - m * (vf ^ 2) / 2) / d ) .SubstituteEqLs(vals) .AssertEqTo(fk == 22500.0); // t (d == (vi + vf) * t / 2) .IsolateVariable(t) .AssertEqTo(t == 2 * d / (vf + vi)) .SubstituteEqLs(vals) .AssertEqTo(t == 1.3333333333333334e-4); } [Fact] public void PSE_5E_P7_41() { // A 2.00-kg block is attached to a spring of force constant // 500 N/m, as shown in Figure 7.10. The block is pulled // 5.00 cm to the right of equilibrium and is then released // from rest. Find the speed of the block as it passes // through equilibrium if // (a) the horizontal surface is frictionless // (b) the coefficient of friction between the block and the surface is 0.350. var ΣW = new Symbol("ΣW"); var Kf = new Symbol("Kf"); var Ki = new Symbol("Ki"); var m = new Symbol("m"); var d = new Symbol("d"); var n = new Symbol("n"); var g = new Symbol("g"); var k = new Symbol("k"); var vf = new Symbol("vf"); var vi = new Symbol("vi"); var fk = new Symbol("fk"); var W_f = new Symbol("W_f"); var W_s = new Symbol("W_s"); var μk = new Symbol("μk"); var xi = new Symbol("xi"); var xf = new Symbol("xf"); var eqs = and( n == m * g, fk == μk * n, Kf == m * (vf ^ 2) / 2, Ki == m * (vi ^ 2) / 2, W_f == -fk * d, W_s == k * (xi ^ 2) / 2 - k * (xf ^ 2) / 2, ΣW == Kf - Ki, ΣW == W_f + W_s, m != 0 ); var vals = new List() { m == 2.0, k == 500, xi == 0.05, xf == 0.0, vi == 0, d == 0.05, g == 9.8 }; eqs .EliminateVariables(Kf, Ki, ΣW, W_f, W_s, n, fk) .IsolateVariable(vf) .LogicalExpand().SimplifyEquation().SimplifyLogical().CheckVariable(m) .AssertEqTo( or( and( vf == sqrt(-2 * m * (-m * (vi ^ 2) / 2 + k * (xf ^ 2) / 2 - k * (xi ^ 2) / 2 + d * g * m * μk)) / m, m != 0 ), and( vf == -sqrt(-2 * m * (-m * (vi ^ 2) / 2 + k * (xf ^ 2) / 2 - k * (xi ^ 2) / 2 + d * g * m * μk)) / m, m != 0))) .SubstituteEqLs(vals).SubstituteEq(μk == 0) .AssertEqTo(or(vf == 0.79056941504209488, vf == -0.79056941504209488)); eqs .EliminateVariables(Kf, Ki, ΣW, W_f, W_s, n, fk) .IsolateVariable(vf) .LogicalExpand().SimplifyEquation().SimplifyLogical().CheckVariable(m) .SubstituteEqLs(vals).SubstituteEq(μk == 0.35) .AssertEqTo(or(vf == 0.53103672189407025, vf == -0.53103672189407025)); } [Fact] public void PSE_5E_P7_55() { // A baseball outfielder throws a 0.150-kg baseball at a // speed of 40.0 m/s and an initial angle of 30.0°. What is // the kinetic energy of the baseball at the highest point of // the trajectory? var vx = new Symbol("vx"); var vi = new Symbol("vi"); var th = new Symbol("th"); var m = new Symbol("m"); var K = new Symbol("K"); var vals = new List() { m == 0.15, vi == 40.0, th == (30).ToRadians() }; var eqs = and( vx == vi * cos(th), K == m * (vx ^ 2) / 2 ); eqs .EliminateVariables(vx) .AssertEqTo(K == (cos(th) ^ 2) * m * (vi ^ 2) / 2) .SubstituteEqLs(vals) .AssertEqTo(K == 90.0); } [Fact] public void PSE_5E_E8_2() { // A ball of mass m is dropped from a height h above the // ground, as shown in Figure 8.6. // (a) Neglecting air resistance, determine the speed of // the ball when it is at a height ya bove the ground. // (b) Determine the speed of the ball at y if at the instant of // release it already has an initial speed vi at the initial altitude h. var m = new Symbol("m"); var yi = new Symbol("yi"); var yf = new Symbol("yf"); var vi = new Symbol("vi"); var vf = new Symbol("vf"); var Ki = new Symbol("Ki"); var Kf = new Symbol("Kf"); var Ugi = new Symbol("Ugi"); var Ugf = new Symbol("Ugf"); var ΣUi = new Symbol("ΣUi"); var ΣUf = new Symbol("ΣUf"); var Ei = new Symbol("Ei"); var Ef = new Symbol("Ef"); var g = new Symbol("g"); var h = new Symbol("h"); var y = new Symbol("y"); var eqs = and( Ki == m * (vi ^ 2) / 2, Kf == m * (vf ^ 2) / 2, Ugi == m * g * yi, Ugf == m * g * yf, ΣUi == Ugi, ΣUf == Ugf, Ei == Ki + ΣUi, Ef == Kf + ΣUf, Ei == Ef ); var vals = new List() { yi == h, yf == y }; // vf, vi == 0 eqs .EliminateVariables(Ugi, Ugf, ΣUi, ΣUf, Ki, Kf, Ei, Ef) .MultiplyBothSidesBy(1 / m) .AlgebraicExpand() .IsolateVariable(vf) .SubstituteEqLs(vals) .SubstituteEq(vi == 0) .AssertEqTo( or( vf == -sqrt(2 * (g * h - g * y)), vf == sqrt(2 * (g * h - g * y)) )); // vf eqs .EliminateVariables(Ugi, Ugf, ΣUi, ΣUf, Ki, Kf, Ei, Ef) .MultiplyBothSidesBy(1 / m) .AlgebraicExpand() .IsolateVariable(vf) .SubstituteEqLs(vals) .AssertEqTo( or( vf == -sqrt(2 * (g * h + (vi ^ 2) / 2 - g * y)), vf == sqrt(2 * (g * h + (vi ^ 2) / 2 - g * y)) )); } [Fact] public void PSE_5E_E8_3() { // A pendulum consists of a sphere of mass mattached to a light // cord of length L, as shown in Figure 8.7. The sphere is released // from rest when the cord makes an angle thA with the vertical, // and the pivot at P is frictionless. // (a) Find the speed of the sphere when it is at the lowest point B. // (b) What is the tension T_B in the cord at B? // (c) A pendulum of length 2.00 m and mass 0.500 kg // is released from rest when the cord makes an angle of 30.0° // with the vertical. Find the speed of the sphere and the tension // in the cord when the sphere is at its lowest point. var m = new Symbol("m"); var yi = new Symbol("yi"); var yf = new Symbol("yf"); var vi = new Symbol("vi"); var vf = new Symbol("vf"); var Ki = new Symbol("Ki"); var Kf = new Symbol("Kf"); var Ugi = new Symbol("Ugi"); var Ugf = new Symbol("Ugf"); var ΣUi = new Symbol("ΣUi"); var ΣUf = new Symbol("ΣUf"); var Ei = new Symbol("Ei"); var Ef = new Symbol("Ef"); var g = new Symbol("g"); var L = new Symbol("L"); var thA = new Symbol("thA"); var ar_f = new Symbol("ar_f"); var r = new Symbol("r"); var ΣFr = new Symbol("ΣFr"); var T_f = new Symbol("T_f"); var vf_sq = new Symbol("vf_sq"); var eqs = and( Ki == m * (vi ^ 2) / 2, Kf == m * (vf ^ 2) / 2, Ugi == m * g * yi, Ugf == m * g * yf, ΣUi == Ugi, ΣUf == Ugf, Ei == Ki + ΣUi, Ef == Kf + ΣUf, Ei == Ef, ar_f == (vf ^ 2) / r, ΣFr == T_f - m * g, ΣFr == m * ar_f ); var vals = new List() { yi == -L * cos(thA), yf == -L, vi == 0, r == L }; var numerical_vals = new List() { L == 2.0, m == 0.5, thA == (30).ToRadians(), g == 9.8 }; // vf eqs .SubstituteEqLs(vals) .EliminateVariables(ar_f, ΣFr, T_f, Ki, Kf, Ugi, Ugf, ΣUi, ΣUf, Ei, Ef) .MultiplyBothSidesBy(1 / m) .AlgebraicExpand() .IsolateVariable(vf) .AssertEqTo( or( vf == -sqrt(2 * (g * L - cos(thA) * g * L)), vf == sqrt(2 * (g * L - cos(thA) * g * L)) ) ) .SubstituteEqLs(numerical_vals).Substitute(3, 3.0) .AssertEqTo( or( vf == -2.2916815161906787, vf == 2.2916815161906787 ) ); // T_f eqs .SubstituteEqLs(vals) .Substitute(vf ^ 2, vf_sq) .EliminateVariables(Ki, Kf, Ugi, Ugf, ΣUi, ΣUf, Ei, Ef, ar_f, ΣFr, vf_sq) .MultiplyBothSidesBy(1 / m) .AlgebraicExpand() .IsolateVariable(T_f) .AssertEqTo( T_f == (3 * g - 2 * cos(thA) * g) * m ); } [Fact] public void PSE_5E_E8_4() { // A 3.00-kg crate slides down a ramp. The ramp is 1.00 m in // length and inclined at an angle of 30.0°, as shown in Figure // 8.8. The crate starts from rest at the top, experiences a // constant frictional force of magnitude 5.00 N, and continues to // move a short distance on the flat floor after it leaves the // ramp. Use energy methods to determine the speed of the // crate at the bottom of the ramp. var m = new Symbol("m"); var yi = new Symbol("yi"); var yf = new Symbol("yf"); var vi = new Symbol("vi"); var vf = new Symbol("vf"); var Ki = new Symbol("Ki"); var Kf = new Symbol("Kf"); var Ugi = new Symbol("Ugi"); var Ugf = new Symbol("Ugf"); var ΣUi = new Symbol("ΣUi"); var ΣUf = new Symbol("ΣUf"); var Ei = new Symbol("Ei"); var Ef = new Symbol("Ef"); var fk = new Symbol("fk"); var W_f = new Symbol("W_f"); var ΔE = new Symbol("ΔE"); var g = new Symbol("g"); var d = new Symbol("d"); var θ = new Symbol("θ"); var eqs = and( yi == d * sin(θ), Ki == m * (vi ^ 2) / 2, Kf == m * (vf ^ 2) / 2, Ugi == m * g * yi, Ugf == m * g * yf, ΣUi == Ugi, ΣUf == Ugf, W_f == -fk * d, ΔE == W_f, Ei == Ki + ΣUi, Ef == Kf + ΣUf, Ei + ΔE == Ef, m != 0 ); var vals = new List() { m == 3.0, d == 1.0, θ == (30).ToRadians(), fk == 5.0, vi == 0.0, g == 9.8, yf == 0.0 }; eqs .EliminateVariables(Ei, Ef, ΔE, Ki, Kf, ΣUi, ΣUf, W_f, Ugi, Ugf, yi) .IsolateVariable(vf) .LogicalExpand().SimplifyEquation().SimplifyLogical().CheckVariable(m) .AssertEqTo( or( and( vf == -sqrt(2 * m * (-d * fk + m * (vi ^ 2) / 2 - g * m * yf + g * m * d * sin(θ))) / m, m != 0 ), and( vf == sqrt(2 * m * (-d * fk + m * (vi ^ 2) / 2 - g * m * yf + g * m * d * sin(θ))) / m, m != 0 ))) .SubstituteEqLs(vals) .AssertEqTo(or(vf == -2.54296414970142, vf == 2.54296414970142)); } [Fact] public void PSE_5E_Example_8_5() { // A child of mass mrides on an irregularly curved slide of // height as shown in Figure 8.9.The child starts // from rest at the top. // (a) Determine his speed at the bottom, assuming no friction is present. // (b) If a force of kinetic friction acts on the child, how // much mechanical energy does the system lose? Assume that // vf = 3.0 m/s and m = 20.0 kg. var m = new Symbol("m"); var yi = new Symbol("yi"); var yf = new Symbol("yf"); var vi = new Symbol("vi"); var vf = new Symbol("vf"); var Ki = new Symbol("Ki"); var Kf = new Symbol("Kf"); var Ugi = new Symbol("Ugi"); var Ugf = new Symbol("Ugf"); var ΣUi = new Symbol("ΣUi"); var ΣUf = new Symbol("ΣUf"); var Ei = new Symbol("Ei"); var Ef = new Symbol("Ef"); var fk = new Symbol("fk"); var W_f = new Symbol("W_f"); var ΔE = new Symbol("ΔE"); var g = new Symbol("g"); var d = new Symbol("d"); var eqs = and( Ki == m * (vi ^ 2) / 2, Kf == m * (vf ^ 2) / 2, Ugi == m * g * yi, Ugf == m * g * yf, ΣUi == Ugi, ΣUf == Ugf, W_f == -fk * d, ΔE == W_f, Ei == Ki + ΣUi, Ef == Kf + ΣUf, Ei + ΔE == Ef); { var vals = new List() { yi == 2.0, yf == 0, vi == 0, fk == 0, g == 9.8 }; var zeros = vals.Where(eq => eq.b == 0).ToList(); // vf eqs .SubstituteEqLs(zeros) .EliminateVariables(Ei, Ef, ΔE, Ki, Kf, ΣUi, ΣUf, W_f, Ugi, Ugf) .MultiplyBothSidesBy(1 / m) .IsolateVariable(vf) .AssertEqTo( or( vf == -sqrt(2 * g * yi), vf == sqrt(2 * g * yi))) .SubstituteEqLs(vals) .AssertEqTo( or( vf == -6.2609903369994111, vf == 6.2609903369994111)); } { var vals = new List() { m == 20.0, yi == 2.0, yf == 0, vi == 0, vf == 3.0, g == 9.8 }; var zeros = vals.Where(eq => eq.b == 0).ToList(); // ΔE eqs .SubstituteEqLs(zeros) .EliminateVariables(fk, Ei, Ef, Ki, Kf, ΣUi, ΣUf, Ugi, Ugf, W_f) .IsolateVariable(ΔE) .AssertEqTo(ΔE == m * (vf ^ 2) / 2 - g * m * yi) .SubstituteEqLs(vals) .AssertEqTo(ΔE == -302.0); } } } } ================================================ FILE: Tests/Tests.csproj ================================================ netcoreapp2.2 false