{"id":26541348,"url":"https://github.com/project-eutopia/keisan","last_synced_at":"2025-03-22T01:04:07.999Z","repository":{"id":46293670,"uuid":"86110515","full_name":"project-eutopia/keisan","owner":"project-eutopia","description":"A Ruby-based expression parser, evaluator, and programming language","archived":false,"fork":false,"pushed_at":"2024-03-04T02:53:44.000Z","size":930,"stargazers_count":72,"open_issues_count":0,"forks_count":10,"subscribers_count":4,"default_branch":"master","last_synced_at":"2024-04-25T07:41:44.902Z","etag":null,"topics":["calculator","equation","formula","math","programming-language","ruby","symbolic-math"],"latest_commit_sha":null,"homepage":"","language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/project-eutopia.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"MIT-LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2017-03-24T21:06:10.000Z","updated_at":"2024-06-19T09:53:38.318Z","dependencies_parsed_at":"2024-06-19T09:53:34.825Z","dependency_job_id":"41997293-8661-49fb-ab91-d2f01c6adfd8","html_url":"https://github.com/project-eutopia/keisan","commit_stats":{"total_commits":467,"total_committers":3,"mean_commits":"155.66666666666666","dds":"0.0064239828693790635","last_synced_commit":"9ec0c9410dfe5a2292cb1003ad8c6e9959f01307"},"previous_names":[],"tags_count":24,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/project-eutopia%2Fkeisan","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/project-eutopia%2Fkeisan/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/project-eutopia%2Fkeisan/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/project-eutopia%2Fkeisan/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/project-eutopia","download_url":"https://codeload.github.com/project-eutopia/keisan/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":244890081,"owners_count":20527033,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["calculator","equation","formula","math","programming-language","ruby","symbolic-math"],"created_at":"2025-03-22T01:03:20.188Z","updated_at":"2025-03-22T01:04:07.980Z","avatar_url":"https://github.com/project-eutopia.png","language":"Ruby","readme":"# Keisan\n\n[![Gem Version](https://badge.fury.io/rb/keisan.svg)](https://badge.fury.io/rb/keisan)\n[![Build Status](https://travis-ci.org/project-eutopia/keisan.png?branch=master)](https://travis-ci.org/project-eutopia/keisan)\n[![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](https://opensource.org/licenses/MIT)\n[![Hakiri](https://hakiri.io/github/project-eutopia/keisan/master.svg)](https://hakiri.io/github/project-eutopia/keisan)\n[![Maintainability](https://api.codeclimate.com/v1/badges/760e213d5ea81bca4480/maintainability)](https://codeclimate.com/github/project-eutopia/keisan/maintainability)\n[![Coverage Status](https://coveralls.io/repos/github/project-eutopia/keisan/badge.svg?branch=master)](https://coveralls.io/github/project-eutopia/keisan?branch=master)\n\nKeisan ([計算, to calculate](https://en.wiktionary.org/wiki/%E8%A8%88%E7%AE%97#Japanese)) is a Ruby library for parsing equations into an abstract syntax tree.\nThis allows for safe evaluation of string representations of mathematical/logical expressions.\nIt has support for variables, functions, conditionals, and loops, making it a [Turing complete](https://github.com/project-eutopia/keisan/blob/master/spec/keisan/turing_machine_spec.rb) programming language.\n\n## Installation\n\nAdd this line to your application's Gemfile:\n\n```\ngem 'keisan'\n```\n\nAnd then execute:\n\n    $ bundle\n\nOr install it yourself as:\n\n    $ gem install keisan\n\n## Usage\n\n### REPL\n\nTo try `keisan` out locally, clone this repository and run the executable `bin/keisan` to open up an interactive REPL.\nThe commands you type in to this REPL are relayed to an internal `Keisan::Calculator` class and displayed back to you.\n\n![alt text](screenshots/repl.png \"Keisan built-in REPL\")\n\n\n### Calculator class\n\nThis library is interacted with primarily through the `Keisan::Calculator` class.\nThe `evaluate` method evaluates an expression by parsing it into an abstract syntax tree (AST), and evaluating it.\nThere is also a `simplify` method that allows undefined variables and functions to exist, and will just return the simplified AST.\n\n```ruby\ncalculator = Keisan::Calculator.new\ncalculator.evaluate(\"15 + 2 * (1 + 3)\")\n#=\u003e 23\ncalculator.simplify(\"1*(0*2+x*g(t))\").to_s\n#=\u003e \"x*g(t)\"\n```\n\nFor users who want access to the parsed abstract syntax tree, you can use the `ast` method to parse any given expression.\n\n```ruby\ncalculator = Keisan::Calculator.new\nast = calculator.ast(\"x**2+1\")\nast.class\n#=\u003e Keisan::AST::Plus\nast.to_s\n#=\u003e \"(x**2)+1\"\nast.children.map(\u0026:to_s)\n#=\u003e [\"x**2\", \"1\"]\n```\n\n#### Caching AST results\n\nComputing the AST from a string takes some non-zero amount of time.\nFor applications of this gem that evaluate some set of fixed expressions (possibly with different variable values, but with fixed ASTs), it might be worthwhile to cache the ASTs for faster computation.\nTo accomplish this, you can use the `Keisan::AST::Cache` class.\nPassing an instance of this class into the `Calculator` will mean everytime a new expression is encountered it will compute the AST and store it in this cache for retrieval next time the expression is encountered.\n\n```ruby\ncache = Keisan::AST::Cache.new\n# Note: if you don't want to create the Cache instance, you can just pass `cache: true` here as well\ncalculator = Keisan::Calculator.new(cache: cache)\ncalculator.evaluate(\"exp(-x/T)\", x: 1.0, T: 10)\n#=\u003e 0.9048374180359595\n# This call will use the cached AST for \"exp(-x/T)\"\ncalculator.evaluate(\"exp(-x/T)\", x: 2.0, T: 10)\n#=\u003e 0.8187307530779818\n```\n\nIf you just want to pre-populate the cache with some predetermined values, you can call `#fetch_or_build` on the `Cache` for each instance, `freeze` the cache, then use this frozen cache in your calculator.\nA cache that has been frozen will only fetch from the cache, never write new values to it.\n\n```ruby\ncache = Keisan::AST::Cache.new\ncache.fetch_or_build(\"f(x) + diff(g(x), x)\")\ncache.freeze\n# This calculator will never write new values to the cache, but when\n# evaluating `\"f(x) + diff(g(x), x)\"` will fetch this cached AST.\ncalculator = Keisan::Calculator.new(cache: cache)\n```\n\n##### Specifying variables\n\nPassing in a hash of variable (`name`, `value`) pairs to the `evaluate` method is one way of defining variables\n\n```ruby\ncalculator = Keisan::Calculator.new\ncalculator.evaluate(\"3*x + y**2\", x: -2.5, y: 3)\n#=\u003e 1.5\n```\n\nIt is also possible to define variables in the string expression itself using the assignment `=` operator\n\n```ruby\ncalculator = Keisan::Calculator.new\ncalculator.evaluate(\"x = 10*n\", n: 2)\ncalculator.evaluate(\"3*x + 1\")\n#=\u003e 61\n```\n\nTo perform multiple assignments, lists can be used\n\n```ruby\ncalculator = Keisan::Calculator.new\ncalculator.evaluate(\"x = [1, 2]\")\ncalculator.evaluate(\"[x[1], y] = [11, 22]\")\ncalculator.evaluate(\"x\")\n#=\u003e [1, 11]\ncalculator.evaluate(\"y\")\n#=\u003e 22\n```\n\n\n##### Specifying functions\n\nJust like variables, functions can be defined by passing a `Proc` object as follows\n\n```ruby\ncalculator = Keisan::Calculator.new\ncalculator.evaluate(\"2*f(1+2) + 4\", f: Proc.new {|x| x**2})\n#=\u003e 22\n```\n\nNote that functions work in both regular (`f(x)`) and postfix (`x.f()`) notation, where for example `a.f(b,c)` is translated internally to `f(a,b,c)`.\nThe postfix notation requires the function to take at least one argument, and if there is only one argument to the function then the braces can be left off: `x.f`.\n\n```ruby\ncalculator = Keisan::Calculator.new\ncalculator.evaluate(\"[1,3,5,7].size\")\n#=\u003e 4\ncalculator.define_function!(\"f\", Proc.new {|x| [[x-1,x+1], [x-2,x,x+2]]})\ncalculator.evaluate(\"4.f[0]\")\n#=\u003e [3,5]\ncalculator.evaluate(\"4.f[1].size\")\n#=\u003e 3\n```\n\nLike variables, it is also possible to define functions in the string expression itself using the assignment operator `=`\n\n```ruby\ncalculator = Keisan::Calculator.new\ncalculator.evaluate(\"f(x) = n*x\", n: 10) # n is local to this definition only\ncalculator.evaluate(\"f(3)\")\n#=\u003e 30\ncalculator.evaluate(\"f(0-a)\", a: 2)\n#=\u003e -20\ncalculator.evaluate(\"n\") # n only exists in the definition of f(x)\n#=\u003e Keisan::Exceptions::UndefinedVariableError: n\ncalculator.evaluate(\"includes(a, element) = a.reduce(false, found, x, found || (x == element))\")\ncalculator.evaluate(\"[3, 9].map(x, [1, 3, 5].includes(x))\").value\n#=\u003e [true, false]\n```\n\nThis form even supports recursion, but you must explicitly allow it.\n\n```ruby\ncalculator = Keisan::Calculator.new\ncalculator = Keisan::Calculator.new(allow_recursive: false)\ncalculator.evaluate(\"my_fact(n) = if (n \u003e 1, n*my_fact(n-1), 1)\")\n#=\u003e Keisan::Exceptions::InvalidExpression: Unbound function definitions are not allowed by current context\n\ncalculator = Keisan::Calculator.new(allow_recursive: true)\ncalculator.evaluate(\"my_fact(n) = if (n \u003e 1, n*my_fact(n-1), 1)\")\ncalculator.evaluate(\"my_fact(4)\")\n#=\u003e 24\ncalculator.evaluate(\"my_fact(5)\")\n#=\u003e 120\n```\n\n##### Multiple lines and blocks\n\nKeisan understands strings which contain multiple lines.\nIt will evaluate each line separately, and the last line will be the the result of the total evaluation.\nLines can be separated by newlines or semi-colons.\n\n```ruby\ncalculator = Keisan::Calculator.new\ncalculator.evaluate(\"x = 2; y = 5\\n x+y\")\n#=\u003e 7\n```\n\nThe use of curly braces `{}` can be used to create block which has a new closure where variable definitions are local to the block itself.\nInside a block, external variables are still visible and re-assignable, but new variable definitions remain local.\n\n```ruby\ncalculator = Keisan::Calculator.new\ncalculator.evaluate(\"x = 10; y = 20\")\ncalculator.evaluate(\"{a = 100; x = 15; a+x+y}\")\n#=\u003e 135\ncalculator.evaluate(\"x\")\n#=\u003e 15\ncalculator.evaluate(\"a\")\n#=\u003e Keisan::Exceptions::UndefinedVariableError: a\n```\n\nBy default assigning to a variable or function will bubble up to the first definition available in the parent scopes.\nTo assign to a local variable instead of modifying an existing variable out of the closure, you can use the `let` keyword.\nThe difference is illustrated below.\n\n```ruby\ncalculator = Keisan::Calculator.new\ncalculator.evaluate(\"x = 1; {x = 2}; x\")\n#=\u003e 2\ncalculator.evaluate(\"x = 11; {let x = 12}; x\")\n#=\u003e 11\n```\n\n##### Comments\n\nWhen working with multi-line blocks of code, sometimes comments are useful to include.\nComments are parts of a string from the `#` character to the end of a line (indicated by a newline character `\"\\n\"`).\n\n```\ncalculator = Keisan::Calculator.new\ncalculator.evaluate(\"\"\"\n  # This is a comment\n  x = 'foo'\n  x += '#bar' # Notice that `#` inside strings is not part of the comment\n  x # Should print 'foo#bar'\n\"\"\")\n#=\u003e \"foo#bar\"\n```\n\n##### Lists\n\nJust like in Ruby, lists can be defined using square brackets, and indexed using square brackets\n\n```ruby\ncalculator = Keisan::Calculator.new\ncalculator.evaluate(\"[2, 3, 5, 8]\")\n#=\u003e [2, 3, 5, 8]\ncalculator.evaluate(\"[[1,2,3],[4,5,6],[7,8,9]][1][2]\")\n#=\u003e 6\ncalculator.evaluate(\"a = [1,2,3]\")\ncalculator.evaluate(\"a[1] += 10*a[2]\")\ncalculator.evaluate(\"a\")\n#=\u003e [1, 32, 3]\n```\n\nThey can also be concatenated using the `+` operator\n\n```ruby\ncalculator = Keisan::Calculator.new\ncalculator.evaluate(\"[3, 5] + [x, x+1]\", x: 10)\n#=\u003e [3, 5, 10, 11]\n```\n\nKeisan also supports the following useful list methods,\n\n```ruby\ncalculator = Keisan::Calculator.new\ncalculator.evaluate(\"[1,3,5].size\")\n#=\u003e 3\ncalculator.evaluate(\"[1,3,5].max\")\n#=\u003e 5\ncalculator.evaluate(\"[1,3,5].min\")\n#=\u003e 1\ncalculator.evaluate(\"[1,3,5].reverse\")\n#=\u003e [5,3,1]\ncalculator.evaluate(\"[[1,2],[3,4]].flatten\")\n#=\u003e [1,2,3,4]\ncalculator.evaluate(\"range(5)\")\n#=\u003e [0,1,2,3,4]\ncalculator.evaluate(\"range(5,10)\")\n#=\u003e [5,6,7,8,9]\ncalculator.evaluate(\"range(0,10,2)\")\n#=\u003e [0,2,4,6,8]\ncalculator.evaluate(\"[1, 2, 2, 3].uniq\")\n#=\u003e [1,2,3]\ncalculator.evaluate(\"[1, 2, 3].difference([2, 3, 4])\")\n#=\u003e [1]\ncalculator.evaluate(\"[1, 2, 3].intersection([2, 3, 4])\")\n#=\u003e [2, 3]\ncalculator.evaluate(\"[1, 2, 3].union([2, 3, 4])\")\n#=\u003e [1, 2, 3, 4]\n```\n\n##### Hashes\n\nKeisan also supports associative arrays (hashes), which maps keys to values.\n\n```ruby\ncalculator = Keisan::Calculator.new\ncalculator.evaluate(\"my_hash = {777: 3*4, \\\"bar\\\": \\\"hello world\\\"}\")\ncalculator.evaluate(\"my_hash[777]\")\n#=\u003e 12\ncalculator.evaluate(\"s = 'ba'\")\ncalculator.evaluate(\"my_hash[s + 'r']\")\n#=\u003e \"hello world\"\ncalculator.evaluate(\"my_hash['baz']\")\n#=\u003e nil\ncalculator.evaluate(\"my_hash['baz'] = 999\")\ncalculator.evaluate(\"my_hash['baz']\")\n#=\u003e 999\n```\n\nThere is also a `to_h` method which converts a list of key value pairs into a hash.\n\n```ruby\ncalculator = Keisan::Calculator.new\ncalculator.evaluate(\"range(1, 6).map(x, [x, x**2]).to_h\")\n#=\u003e {1 =\u003e 1, 2 =\u003e 4, 3 =\u003e 9, 4 =\u003e 16, 5 =\u003e 25}\n```\n\n##### Date and time objects\n\nKeisan supports date and time objects like in Ruby.\nYou create a date object using either the method `date` (either a string to be parsed, or year, month, day numerical arguments) or `today`.\nThey support methods `year`, `month`, `day`, `weekday`, `strftime`, and `to_time` to convert to a time object.\n`epoch_days` computes the number of days since Unix epoch (Jan 1, 1970).\n\n```ruby\ncalculator = Keisan::Calculator.new\ncalculator.evaluate(\"x = 11\")\ncalculator.evaluate(\"(5 + date(2018, x, 2*x)).day\")\n#=\u003e 27\ncalculator.evaluate(\"today() \u003e date(2018, 11, 1)\")\n#=\u003e true\ncalculator.evaluate(\"date('1999-12-31').to_time + 10\")\n#=\u003e Time.new(1999, 12, 31, 0, 0, 10)\ncalculator.evaluate(\"date(1970, 1, 15).epoch_days\")\n#=\u003e 14\n```\n\nTime objects are created using `time` (either a string to be parsed, or year, month, day, hour, minute, second arguments) or `now`.\nThey support methods `year`, `month`, `day`, `hour`, `minute`, `second`, `weekday`, `strftime`, and `to_date` to convert to a date object.\n`epoch_seconds` computes the number of seconds since Unix epoch (00:00:00 on Jan 1, 1970).\n\n```ruby\ncalculator = Keisan::Calculator.new\ncalculator.evaluate(\"time(2018, 11, 22, 12, 0, 0).to_date \u003c= date(2018, 11, 22)\")\n#=\u003e true\ncalculator.evaluate(\"time('2000-4-15 12:34:56').minute\")\n#=\u003e 34\ncalculator.evaluate(\"time('5000-10-10 20:30:40').strftime('%b %d, %Y')\")\n#=\u003e \"Oct 10, 5000\"\ncalculator.evaluate(\"time(1970, 1, 1, 2, 3, 4).epoch_seconds\")\n#=\u003e 7384\n```\n\n##### Functional programming methods\n\nKeisan also supports the basic functional programming operators `map` (or `collect`), `filter` (or `select`), and `reduce` (or `inject`).\n\n```ruby\ncalculator = Keisan::Calculator.new\ncalculator.evaluate(\"map([1,3,5], x, 2*x)\")\n#=\u003e [2,6,10]\ncalculator.simplify(\"{'a': 1, 'b': 3, 'c': 5}.collect(k, v, y*v**2)\").to_s\n#=\u003e \"[y,9*y,25*y]\"\n\ncalculator.evaluate(\"[1,2,3,4].select(x, x % 2 == 0)\")\n#=\u003e [2,4]\ncalculator.evaluate(\"filter({'a': 1, 'bb': 4, 'ccc': 9}, k, v, k.size == 2)\")\n#=\u003e {\"bb\" =\u003e 4}\n\ncalculator.evaluate(\"[1,2,3,4,5].inject(1, total, x, total*x)\")\n#=\u003e 120\ncalculator.evaluate(\"{'foo': 'hello', 'bar': ' world'}.reduce('', res, k, v, res + v)\")\n#=\u003e \"hello world\"\n```\n\n##### Logical operations\n\n`keisan` understands basic boolean logic operators, like `\u003c`, `\u003c=`, `\u003e`, `\u003e=`, `\u0026\u0026`, `||`, `!`, so calculations like the following are possible\n\n```ruby\ncalculator = Keisan::Calculator.new\ncalculator.evaluate(\"1 \u003e 0\")\n#=\u003e true\ncalculator.evaluate(\"!!!true\")\n#=\u003e false\ncalculator.evaluate(\"x \u003e= 0 \u0026\u0026 x \u003c 10\", x: 5)\n#=\u003e true\n```\n\nThere is also a useful ternary `if` function defined\n\n```ruby\ncalculator = Keisan::Calculator.new\ncalculator.evaluate(\"2 + if(1 \u003e 0, 10, 29)\")\n#=\u003e 12\n```\n\nFor looping, you can use the basic `while` loop, which has an expression that evaluates to a boolean as the first argument, and any expression in the second argument.\nOne can use the keywords `break` and `continue` to control loop flow as well.\n\n```ruby\ncalculator = Keisan::Calculator.new\ncalculator.evaluate(\"my_sum(a) = {let i = 0; let total = 0; while(i \u003c a.size, {total += a[i]; i += 1}); total}\")\ncalculator.evaluate(\"my_sum([1,3,5,7,9])\")\n#=\u003e 25\ncalculator.evaluate(\"has_element(a, x) = {let i=0; let found=false; while(i\u003ca.size, if(a[i] == x, found = true; break); i+=1); found}\")\ncalculator.evaluate(\"[2, 3, 7, 11].has_element(11)\")\n#=\u003e true\n```\n\n##### Bitwise operations\n\nThe basic bitwise operations, NOT `~`, OR `|`, XOR `^`, AND `\u0026`, and left/right bitwise shifts (`\u003c\u003c` and `\u003e\u003e`) are also available for use\n\n```ruby\ncalculator = Keisan::Calculator.new\ncalculator.evaluate(\"0b00001111 \u0026 0b10101010\")\n#=\u003e 10\n```\n\n##### String\n\n`keisan` also can parse in strings, and access the characters by index\n\n```ruby\ncalculator = Keisan::Calculator.new\ncalculator.evaluate(\"'hello'[1]\")\n#=\u003e \"e\"\n```\n\n##### Binary, octal, and hexadecimal numbers\n\nUsing the prefixes `0b`, `0o`, and `0x` (standard in Ruby) indicates binary, octal, and hexadecimal numbers respectively.\n\n```ruby\ncalculator = Keisan::Calculator.new\ncalculator.evaluate(\"0b1100\")\n#=\u003e 12\ncalculator.evaluate(\"0o775\")\n#=\u003e 509\ncalculator.evaluate(\"0x1f0\")\n#=\u003e 496\n```\n\n##### Random numbers\n\n`keisan` has a couple methods for doing random operations, `rand` and `sample`.  For example,\n\n```ruby\ncalculator = Keisan::Calculator.new\n(0...10).include? calculator.evaluate(\"rand(10)\")\n#=\u003e true\n[2,4,6,8].include? calculator.evaluate(\"sample([2, 4, 6, 8])\")\n#=\u003e true\n```\n\nIf you want reproducibility, you can pass in your own `Random` object to the calculator's context.\n\n```ruby\ncalculator1 = Keisan::Calculator.new(context: Keisan::Context.new(random: Random.new(1234)))\ncalculator2 = Keisan::Calculator.new(context: Keisan::Context.new(random: Random.new(1234)))\n5.times.map {calculator1.evaluate(\"rand(1000)\")}\n#=\u003e [815, 723, 294, 53, 204]\n5.times.map {calculator2.evaluate(\"rand(1000)\")}\n#=\u003e [815, 723, 294, 53, 204]\n```\n\n##### Builtin variables and functions\n\n`keisan` includes all standard methods given by the Ruby `Math` class.\n\n```ruby\ncalculator = Keisan::Calculator.new\ncalculator.evaluate(\"log10(1000)\")\n#=\u003e 3.0\n```\n\nFurthermore, the constants `PI`, `E`, `I`, and `INF` are included.\n\n```ruby\ncalculator = Keisan::Calculator.new\ncalculator.evaluate(\"E**(I*PI)+1\")\n#=\u003e (0.0+0.0i)\n```\n\nThere is a `replace` method that can replace instances of a variable in an expression with another expression.  The form is `replace(original_expression, variable_to_replace, replacement_expression)`.  Before the replacement is carried out, the `original_expression` and `replacement_expression` are `evaluate`d, then instances in the original expression of the given variable are replaced by the replacement expression.\n\n```ruby\ncalculator = Keisan::Calculator.new\ncalculator.evaluate(\"replace(x**2, x, 3)\")\n#=\u003e 9\n```\n\nWhen using `Calculator` class, all variables must be replaced before an expression can be calculated, but the ability to replace any expression is useful when working directly with the AST.\n\n```ruby\nast = Keisan::AST.parse(\"replace(replace(x**2 + y**2, x, sin(theta)), y, cos(theta))\")\nast.evaluate.to_s\n#=\u003e \"(sin(theta)**2)+(cos(theta)**2)\"\n```\n\nThe derivative operation is also builtin to Keisan as the `diff` function.\n\n```ruby\ncalculator = Keisan::Calculator.new\ncalculator.evaluate(\"diff(4*x, x)\")\n#=\u003e 4\ncalculator.evaluate(\"replace(diff(4*x**2, x), x, 3)\")\n#=\u003e 24\n```\n\nThis also works intelligently with user defined functions.\n\n```ruby\ncalculator = Keisan::Calculator.new\ncalculator.evaluate(\"f(x, y) = x**2 + y\")\ncalculator.simplify(\"diff(f(2*t, t+1), t)\").to_s\n#=\u003e \"1+(8*t)\"\ncalculator.evaluate(\"replace(diff(f(2*t, t+1), t), t, 3)\")\n#=\u003e 1+8*3\n```\n\nThere is also a `puts` function that can be used to output the result of an expression to STDOUT.\n\n```ruby\ncalculator = Keisan::Calculator.new\ncalculator.evaluate(\"x = 5\")\ncalculator.evaluate(\"puts x**2\") # prints \"25\\n\" to STDOUT\n```\n\n\n## Supported elements/operators\n\n`keisan` supports the following operators and elements.\n\n#### Numbers, variables, brackets, functions, lists, hashes\n- `150`, `-5.67`, `6e-5`: regular numbers\n- `x`, `_myvar1`: variables\n- `(` and `)`: round brackets for grouping parts to evaluate first\n- `f(x,y,z)`, `my_function(max([2.5, 5.5]))`, `[2,4,6,8].size`: functions using `(` `)` brackets (optional if using postfix notation and only takes a single argument)\n- `[0, 3, 6, 9]`: square brackets with comma separated values to denote lists\n- `{'foo': 11, 'bar': 22}`: curly brackets containing key/value pairs creates a hash\n\n#### Arithmetic operators\n- `+`, `-`, `*`, `/`: regular arithmetic operators\n- `**`: Ruby style exponent notation (to avoid conflict with bitwise xor `^`)\n- `%`: Ruby modulo operator, sign of `a % b` is same as sign of `b`\n- `+`, `-`: Unary plus and minus\n\n#### Logical operators\n- `\u003c`, `\u003e`, `\u003c=`, `\u003e=`: comparison operators\n- `==` and `!=`: logical equality check operators\n- `\u0026\u0026` and `||`: logical operators, **and** and **or**\n- `!`: unary logical not\n\n#### Bitwise operators\n- `\u0026`, `|`, `^`: bitwise **and**, **or**, **xor** operators\n- `\u003c\u003c`, `\u003e\u003e` bitwise shift operators\n- `~`: unary bitwise not\n\n#### Indexing of arrays/hashes\n- `list[i]`: for accessing elements in an array\n- `hash[k]`: for accessing elements in a hash\n\n#### Assignment\n- `=`: can be used to define variables and functions\n- `+=`: can be used in combination with operators above\n\n\n## Development\n\nAfter checking out the repository, run `bin/setup` to install dependencies.\nThen, run `rake spec` to run the tests.\nYou can also run `bin/console` for an interactive prompt that will allow you to experiment with the library pre-loaded.\n\nTo install this gem onto your local machine, run `bundle exec rake install`.\nTo release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).\n\n## Contributing\n\nBug reports and pull requests are welcome on GitHub at https://github.com/project-eutopia/keisan.\nIf there is any functionality you would like (e.g. new functions), feel free to open a [new issue](https://github.com/project-eutopia/keisan/issues/new).\n","funding_links":[],"categories":["Ruby"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fproject-eutopia%2Fkeisan","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fproject-eutopia%2Fkeisan","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fproject-eutopia%2Fkeisan/lists"}