{"id":15475833,"url":"https://github.com/mooreniemi/graph-function","last_synced_at":"2025-04-22T14:11:10.572Z","repository":{"id":56875170,"uuid":"69043761","full_name":"mooreniemi/graph-function","owner":"mooreniemi","description":":chart_with_upwards_trend: graphing your ruby functions","archived":false,"fork":false,"pushed_at":"2016-12-10T19:57:12.000Z","size":527,"stargazers_count":38,"open_issues_count":3,"forks_count":1,"subscribers_count":2,"default_branch":"master","last_synced_at":"2024-04-24T01:02:34.623Z","etag":null,"topics":["asymptotic","big-o","comparison","gnuplot","kata","ruby"],"latest_commit_sha":null,"homepage":null,"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/mooreniemi.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2016-09-23T16:50:30.000Z","updated_at":"2024-01-09T23:25:15.000Z","dependencies_parsed_at":"2022-08-20T22:00:37.768Z","dependency_job_id":null,"html_url":"https://github.com/mooreniemi/graph-function","commit_stats":null,"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mooreniemi%2Fgraph-function","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mooreniemi%2Fgraph-function/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mooreniemi%2Fgraph-function/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mooreniemi%2Fgraph-function/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mooreniemi","download_url":"https://codeload.github.com/mooreniemi/graph-function/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":250255700,"owners_count":21400410,"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":["asymptotic","big-o","comparison","gnuplot","kata","ruby"],"created_at":"2024-10-02T03:20:33.874Z","updated_at":"2025-04-22T14:11:10.555Z","avatar_url":"https://github.com/mooreniemi.png","language":"Ruby","readme":"```\n                        .-.      / \\        _\n            ^^         /   \\    /^./\\__   _/ \\\n          _        .--'\\/\\_ \\__/.      \\ /    \\  ^^  ___\n         / \\_    _/ ^      \\/  __  :'   /\\/\\  /\\  __/   \\\n        /    \\  /    .'   _/  /  \\   ^ /    \\/  \\/ .`'\\_/\\\n       /\\/\\  /\\/ :' __  ^/  ^/    `--./.'  ^  `-.\\ _    _:\\ _\n      /    \\/  \\  _/  \\-' __/.' ^ _   \\_   .'\\   _/ \\ .  __/ \\\n    /\\  .-   `. \\/     \\ / -.   _/ \\ -. `_/   \\ /    `._/  ^  \\\n   /  `-.__ ^   / .-'.--'    . /    `--./ .-'  `-.  `-. `.  -  `.\n @/        `.  / /      `-.   /  .-'   / .   .'   \\    \\  \\  .-  \\%\n @(88%@)@%% @)\u0026@\u0026(88\u0026@.-_=_-=_-=_-=_-=_.8@% \u0026@\u0026\u00268(8%@%8)(8@%8 8%@)%\n @88:::\u0026(\u00268\u0026\u00268::JGS:\u0026`.~-_~~-~~_~-~_~-~~=.'@(\u0026%::::%@8\u00268)::\u0026#@8::::\n `::::::8%@@%:::::@%\u00268:`.=~~-.~~-.~~=..~'8::::::::\u0026@8:::::\u00268::::::'\n  `::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::'\n\n```\n\n# Graph::Function\n\nThis gem's goal is to make it easy to compare the [asymptotic performance](https://en.wikipedia.org/wiki/Asymptotic_analysis) of two or more functions via graphing.\n\nWhen I work on katas and exercises I found I often wanted to compare my implementations. After doing so a half dozen times I noticed some patterns, and figured it'd be valuable to capture those into an easier API to work with. While working on a kata I like the immediacy of replotting back on x11, but because of gnuplot's structure it is just as easy to get images or html canvas graphs.\n\nAs a secondary performance metric, you can also [graph total allocated memsize](#graphing-memory).\n\n## Installation\n\nBecause this gem depends on `gnuplot` and `xquartz`, we need to follow their [prereq steps](https://github.com/rdp/ruby_gnuplot#pre-requisites-and-installation):\n\n```\n# these will vary by your system, mine is mac\nbrew install Caskroom/cask/xquartz\nbrew install gnuplot --with-x11\n# verify you have x11\nxpdyinfo | grep version\n```\n\nNow we're set. Add this line to your application's Gemfile:\n\n```ruby\ngem 'graph-function'\n```\n\nAnd then execute:\n\n    $ bundle\n\nOr install it yourself as:\n\n    $ gem install graph-function\n\n## Usage\n\n### TL;DR\n\nFrom the [comparing ints example](examples/comparing_ints.rb):\n\n```ruby\nrequire 'graph/function'\nGraph::Function.as_gif\nGraph::Function::IntsComparison.of(method(:sort), method(:bubble_sort))\n```\n\nProduces:\n\n![comparing ints](examples/comparing_ints.gif)\n\n### Setup\n\nTo set up, you only need the following:\n\n```ruby\nrequire 'graph/function'\nGraph::Function.as_x11\n```\n\nIf you don't want to output to [x11](https://www.xquartz.org/), just set `config.terminal` to a different option. Two convenience methods exist for `gif` and `canvas`:\n\n```ruby\n# by default file will be set to name of the executing file and dumped in its dir\n# or you can set file yourself like so:\nGraph::Function.as_gif(File.expand_path('../comparing_ints.gif', __FILE__))\nGraph::Function.as_canvas(File.expand_path('../comparing_ints.html', __FILE__))\n```\n\nYou can use anything else gnuplot [respects as a terminal](http://mibai.tec.u-ryukyu.ac.jp/~oshiro/Doc/gnuplot_primer/gptermcmp.html), even outputting to just `txt`!\n\n```ruby\nGraph::Function.configure do |config|\n  config.terminal = 'dumb'\n  config.output = File.expand_path('../your_graph_name.txt', __FILE__)\n  config.step = (0..10_000).step(1000).to_a # default value\n  config.trials = 1\nend\n```\n\nIn configuration, you can control the \"step\" size of `x` in the plot. Its default value is `(0..10_000).step(1000).to_a` (`[0, 1000, 2000, 3000, 4000, 5000, 6000, 7000, 8000, 9000, 10000]`) but you can make it as fine or rough grained as you need up to any size.\n\nYou can also set a number of trials over which to average execution times.\n\n### Graphing\n\nThe simplest usage (suitable for a large class of exercises, in my experience) is if you're comparing two functions that take a single argument of `Array[Int]` type:\n\n```ruby\nc = YourClass.new # this class has #function_name_one \u0026 #function_name_two\nGraph::Function::IntsComparison.of(c.method(:function_name_one), c.method(:function_name_two))\n# =\u003e will output an xquartz graph\n```\n\n![comparison](spec/graph/two_func.gif)\n\nFor more complex use cases, you'll be creating a `Graph::Function::Comparison` with some generator of data, and executing `#of` with `Method` objects or `Proc`s that operate on the same parameter types\u003csup id=\"a1\"\u003e[1](#f1)\u003c/sup\u003e. (Note because `IntsComparison` *does not need a generator*, `.of` is a class method instead.)\n\n### Graphing Memory\n\nTo graph total allocated memsize rather than execution time, just set the following configuration:\n\n```\nGraph::Function.configure do |config|\n  config.memory = true\nend\n```\n\n### Generators\n\nTo generate values of the type needed by your function, you can write\na generator in Ruby or use the provided dependency\n[Rantly](https://github.com/hayeah/rantly).\n\nHere's an example of a simple Ruby generator, it's just a `Proc` parameterized on `size`:\n\n```ruby\ntiny_int_generator = proc {|size| Array.new(size) { rand(-9...9) } }\ncomparison = Graph::Function::Comparison.new(tiny_int_generator)\n```\n\nFor Rantly usage, there's great documentation on generating many different kinds of data in\ntheir documentation, but here's an example of comparing two functions that\ntake `Hash{String =\u003e Integer}`:\n\n```ruby\n# you must put it in a proc taking size so Graph::Function can increase it\ngenerator = proc {|size| Rantly { dict(size) { [string, integer] } }\ndict_comparison = Graph::Function::Comparison.new(generator)\n# Comparison can take any number of Methods, but for now, 2\ndict_comparison.of(method(:hash_func_one), method(:hash_func_two))\n# =\u003e will output an xquartz graph\n```\n\n![comparison](spec/graph/comparison.gif)\n\nIf you want to make use of more \"real\" fake data, [Faker](https://github.com/stympy/faker) is also included, and can be used like so in your generators:\n\n```ruby\n# again, we need to parameterize our generator with size\nfaker_generator = proc {|size| Rantly(size) { call(Proc.new { Faker::Date.backward(14) }) }\ngraph = Graph::Function::Comparison.new(faker_generator)\ngraph.of(method(:custom_types))\n# =\u003e will output an xquartz graph\n```\n\n![faker](spec/graph/faker.gif)\n\nThe only downside here is that you can't parameterize `Faker`, but you could use random generators to mix it up. Using the above example, `graph-function` won't pass anything into the `faker_generator` but the `size`, so if we want the value to change, we could use `Faker::Date.backward(proc { rand(10) }.call)`.\n\nCheck out the [spec file](spec/graph/function_spec.rb) to see all of these or see [examples](examples/).\n\n### Functions that use `self`\n\nFor graphing functions that operate on `self`, such as `String#upcase`, you must provide a `Method` or `Proc` that wraps the method call. For instance:\n\n```ruby\ngenerator = proc {|size| Rantly { sized(size) { string } } }\n# wrap the call to upcase\ntest_upcase = proc {|s| s.upcase }\ngraph = Graph::Function::Comparison.new(generator)\ngraph.of(test_upcase)\n```\n\n## Development\n\nAfter checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.\n\nTo install this gem onto your local machine, run `bundle exec rake install`. To 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/mooreniemi/graph-function. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.\n\n\n## License\n\nThe gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).\n\n## Footnotes\n\n\u003cb id=\"f1\"\u003e1\u003c/b\u003e Why are we constrained to testing the same parameter types? The intent of this library is to graph _implementations_. Changing parameter types suggests a change in the _behavior_ of the function. That doesn't make for a very productive comparison. [↩](#a1)\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmooreniemi%2Fgraph-function","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmooreniemi%2Fgraph-function","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmooreniemi%2Fgraph-function/lists"}