Posts Tagged ‘ruby’

Smoothly parameterized ease curve

Thursday, May 23rd, 2013

Using polynomial ease curves, it’s easy to create a function f(x) which obtains f(0) = 0 and f(1) = 1 as well as reaching 0 derivatives f’(0) = 0, f’(1) = 0, f”(0) = 0, f”(1) = 0, etc. One can start with a simple cubic polynomial:

f(x) = 3x²-2x³

which achieves first order smoothness (i.e. f’(0) = 0 and f’(1) = 0. Then we can compose this to have higher and higher continuity f(f(x)), f(f(f(x))). This creates a 9th and 27th order polynomials. Here’s a sequence of repeated composing this cubic function. The title shows the order:
polynomial ease curves

We can also derive such a smooth, symmetric polynomial ease curve for any order 2*k+1, k in ℕ+ polynomial. Thus with k we can have finer, albeit incremental, control over the shape of the function. Here’s a ruby script to generate a maple script that will generate the necessary coefficients (there must be a pattern here, but I’m too lazy right now to find it):


#!/opt/local/bin/ruby
# Script to generate Maple code for solving for 2*k+1 order polynomial f(x)
# with value f(0) = 0, f(1) = 2 and dkf/dxk|0 = 0 dcf/dxc|1 = 1, c=1,...,k
smoothness = 3
(1..smoothness).each do |k|
  as = (k+1..2*k+1).map{|c| "a#{c}"}.join(",");
  puts "f := (#{as},x) -> "
  puts "  #{(k+1..2*k+1).map{|c| "a#{c}*x^#{c}"}.join("+")};"
  puts "solve({"
  puts "  f(#{as},0)=0,"
  puts "  f(#{as},1)=1,"
  diffs = (1..k).map do |c|
    "  eval(diff(f(#{as},x),#{(1..c).map{|d| "x"}.join(",")}),x=0)=0,\n"+
    "  eval(diff(f(#{as},x),#{(1..c).map{|d| "x"}.join(",")}),x=1)=0"
  end
  puts "#{diffs.join(",\n")}},"
  puts "  {#{(k+1..2*k+1).map{|c| "a#{c}"}.join(",")}});"
end

Using these coefficients we can plot the first few:
polynomial incremental order smoothness ease fit

These sequences converge on a scaled and shifted Heaviside step function. But we only have an integer-valued parameter. It’d be nice to have a smooth parameter.

If we only cared about this property of the curve getting steeper in the middle as we increase or parameter (while maintaining interpolation and at least C1 continuity, then we could try to do this with a cubic spline. Now we can smoothly increase the steepness, but before reaching the step function the spline curves to far, losing injectivity when treated as a function of x.

spline ease curve The fact the we cannot reproduce the Heaviside function with
a cubic spline should have been obvious anyway. The Heaviside function is loosely a degree ∞ polynomial, with ∞ smoothness at 0 and 1.

We can achieve what we want by taking a function which indeed converges to the Heaviside step function:

f(x) = 1/(1+e-2tx).

We can adapt this to our unit square domain and enforce interpolation for any k:

g(x) = (f(2x-1)-1/2)/(f(1)-1/2)/2+1/2,

or in MATLAB:


g = @(x,t) (1./(1+exp(-2*t*(2*x-1)))-1/2)/(1./(1+exp(-2*t*1))-1/2)/2+1/2);

This function behaves pretty nicely for t in (0,∞), going from a linear function to the Heaviside step function:

Exponential ease curve

To achieve kth order continuity at our endpoints we can just run this function through (i.e. compose with) one of the 2k+1 order polynomials above. That way, with t we smoothly span a whole space of kth order smooth ease curves. For example, if h(x) = 3x&sup2-2x³ then this is h(g(x)):

Exponential smooth ease curve

Backup DVD of television show to .mkv files

Sunday, March 3rd, 2013

I bought some DVDs of a german tv show. With my fancy macbook air, I unfortunately have no way to play them: no DVD drive!

So I dusted off my old laptop and installed vobcopy then I copied each dvd first to the old laptop from the disks using:


vobcopy -m

Then I copied these over to my macbook air. But these are just the raw dvd files (VIDEO_TS/*.VOB), which are rather large and unwieldy. To convert these to .mkv files I know to use Handbrake, but since I want to extract each of the 3 or 4 episodes per DVD, setting up Handbrake for each episode turned out to be too tedious. The presets in Handbrake don’t include options to extract all audio channels and all subtitles by default. Instead I found a ruby script call hb.rb which can set up batch jobs.

So finally to extract my episodes with all subtitles and all audio channels in tact I used:

./hb.rb --verbose --input ~/Downloads/IM_ANGESICHT_DES_VERBRECHENS_D2 --output "~/Downloads/#title#_#pos#.mkv" --min-length 00:10:00 --max-length 00:50:00 --skip-duplicates --audio-copy --skip-commentaries --only-first-track-per-language --autocrop

Homonym translator

Sunday, March 13th, 2011

I wrote a little ruby program that accepts a string and translates as many words as it can to homonyms.
Save this in a file called homotranslate.rb:


#!/usr/local/bin/ruby -w

class Homo
  def initialize(filename)
    lines = File.open(filename, "r").readlines;
    homonyms = lines.collect{|e| e.strip.split(' ')};
    @homo_map = Hash.new;
    homonyms.each{|h| h.each{|e| @homo_map[e] = h - [e]; }; };
  end

  def random_translate(str)
    str.gsub(/\w+/) do |word| 
      lw = word.downcase;
  
      if(@homo_map[lw].nil? || word.size == 0)
        word;
      else
        homonym = @homo_map[lw][rand(@homo_map[lw].size)];
        if(word[0] >= 'A'[0] and word[0] <= 'Z'[0])
          homonym.capitalize!
        end
        homonym;
      end
    end
  end
end


# Default main program
if $0 == __FILE__
  homo = Homo.new("homonyms.txt");
  puts homo.random_translate(ARGF.read);
end
__END__

Then run with:


echo "I owe you." | ruby homotranslate.rb

Or just try it online now:


Random homonym translator web app
Source
ASCII list of homonyms, one set per line, space separated

Ruby plus equals (+=) versus append/concatenation shovel (<<)

Wednesday, September 8th, 2010

I was stunned to watch how slow a recent ruby program was. All it was doing was concatenating a bunch of string literals in a big loop. Originally I was using plus equals:


str = ""
1000.times do |i|
  str += "foo bar"
end

On a whim I tried switching to using an array then joining:


str = ""
str_array = []
1000.times do |i|
  str_array << "foo bar"
end
str = str_array.join

Already this was way faster. I wrote up a little benchmarking program to see just how badly "+=" performs compared to "<<". I compare string +=, to the array set up I have above, and just using "<<" on the string:


str = ""
1000.times do |i|
  str << "foo bar"
end

Here's my little test program.


#!/usr/bin/ruby

power = 20

power.times do |p|
  n = 2**p
  str = ""
  start_time = Time.now
  n.times do |i|
    str += "x"
  end
  duration = Time.now - start_time
  #puts "#{n} string appends took: #{duration}s"
  puts "#{n} #{duration}"
end

power.times do |p|
  n = 2**p
  str3 = ""
  start_time = Time.now
  n.times do |i|
    str3 << "x"
  end
  duration = Time.now - start_time
  puts "#{n} #{duration}"
end

power.times do |p|
  n = 2**p
  str2 = ""
  start_time = Time.now
  str_array = []
  n.times do |i|
    str_array << "x"
  end
  str2 = str_array.join
  duration = Time.now - start_time
  puts "#{n} #{duration}"
end

And here are the results:
ruby plus equals vs append

String += is asymptotically worse than <<. Reading through the ruby doc on strings its clear this is because:


str1 += str2

is syntactic sugar for something like


str1 = str1 + str2

whose "=" creates a new string object, hence the big computational cost.

But why?! I can't think of any reason why "+=" shouldn't be syntactic sugar for "<<". Can you?

Update:
I get it!
Here's two short snippets that illustrate the difference:


a = "x"
b = a
b += "y"
a

Which results in "x"


a = "x"
b = a
b << "y"
a

Which results in "xy"

It's subjective whether x+=y should mean "append y to x" or always be syntactic sugar for "x = x + y". My vote is for the later, which means I must be content that in Ruby these operators do different things and thus have different speeds.

Music clock

Monday, January 11th, 2010

The music clock is an invisible clock. It tells the time through musical
tones. For now, it reads the hour and the minutes mod 12. Hopefully I will be
able to explore this idea and come up with a universally understandable and
recognizable system.

I use ajax requests and ruby and sox on the backend to make this work.

“Recipe organizer and sharing interface”

Monday, January 4th, 2010


'Recipe organizer and sharing interface' beta screenshot

“Recipe organizer and sharing interface” is a recipe organizer and sharing web interface. It’s the Ruby on Rails app I wrote for my final class project in my senior year of college.

Use the beta version of “Recipe organizer and sharing interface” I’m hosting on a CIMS linux server machine.

Escape source code for posting online

Tuesday, November 10th, 2009

I put up an in browser version of my ruby version of escape-copy. You can copy and paste in your code into the form and my program escapes it for you in two ways. In one of the results boxes I just escape the input for <pre> and <code> tags (escape all of the less than and greater than symbols). In the other I escape everything necessary to paste into a generic (unicode?) HTML tag.

Try it out:








List audiobooks and/or music based on directories: Ruby Version

Thursday, November 5th, 2009

I previously posted a bash script to count and print plain text and html listings of a directory: intended for displaying audiobook and music libraries. I recently tried to use the bash script on a large directory and it stalled because sed can’t handle large input. Here’s a ruby version that does the same thing:


#!/usr/bin/ruby -w
usage = "Usage: list [filename] [directory] 
If filename ends with .html then output will be html,
else output will be plain text."
if not ARGV[0] or not ARGV[1]
  puts usage
elsif not File.directory? ARGV[1]
  puts "list: #{ARGV[1]} is not a directory"
  puts usage
else
  dir = ARGV[1]
  # should be quoted?
  plain = (`ls -1 "#{dir}"/*`+"\n")
  if(ARGV[0] =~ /.html$/i)
    plain = plain.split("\n")
    author_count = plain.find_all{|line| line=~/:$/}.length
    title_count = plain.find_all{|line| line=~/[^:]$/}.length
    body = plain.collect do |line|
      line.gsub(
        /(.*)([^:])$/,'      \1\2</br>').gsub(
        /^\s*$/,'    </div>').gsub(
        /.*\/(.*):$/,'    <h3>\1</h3>'+"\n"+'    <div class="books">')
    end
    output = "<html>
  <meta http-equiv='Content-Type' content='text/html; charset=UTF-8'/> 
  <head> 
    <style type='text/css'>
      body {
        font : 10pt verdana;
        background: white;
        width: 95%
      }
      h2 {
        margin: 15px 0px 5px 0px;
      }
      h3 {
        margin: 15px 0px 5px 0px;
      }
      .books {
        border: 1px solid #dddddd;
        padding: 5px 5px 5px 10px;
        background: #eeeeff;
      }
    </style>
  </head>
  <body>
    <h2>#{title_count} titles and #{author_count} authors</h2>
  #{body.join("\n")}</div>
  </body> 
</html>"
  else
    output = plain;
  end
  File.open(ARGV[0], 'w') {|f| f.write(output) }
end

Escape code for html <pre> or <code> tag into clipboard: Ruby Version

Thursday, November 5th, 2009

I previously posted a bash script to speed up posting code in a <pre> or <code> tag in an html page or blog. The script escapes all less than and greater than symbols (< with &lt; and > with &gt;) in a given file then puts the results in the clipboard to facilitate pasting into a text area or text editor. I have now rewritten this idea in ruby. Now you can either cat in a file to the ruby script using pipe, or enter (paste) the input text directly. Save the following the ruby source in a file called escape-copy.rb:


#!/usr/bin/ruby -w
# Usage:
# cat input_file | ruby escape-copy.rb | pbcopy
# or
# ruby escape-copy.rb | pbcopy
# (enter or paste your text then CTRL-D on its own line as EOF)
print ARGF.read.gsub(/</,"<").gsub(/>/,">")

Note: I of course used escape-copy.rb to make posting the source above super easy!

Google images game

Sunday, October 25th, 2009


google images game screen shot

I finished coding a rather simple but addictive and challenging puzzle game, I’m calling the Google images game. The player is presented with the image results of a Google images search for some search word or phrase. The player’s objective is to guess and figure out what that search phrase was. Play it now!

The game is an amalgamation of php, javascript and ruby. The ajax javascript for the game is rather complicated by now to handle all of the gaming options, but it’s all visible in the game’s page source. I use a little ruby program to grab the initial google images thumbnails:


#!/usr/bin/ruby
require 'uri'
require 'net/http'
require 'cgi'

SAFE_VALUES = ["on","moderate","off"]
cgi = CGI.new("html3")
safe = "moderate"
safe = cgi["safe"] if(!cgi["safe"].empty? and SAFE_VALUES.include?(cgi["safe"]))
target = ""
target = cgi["target"] if(!cgi["target"].empty?)

if(!cgi["query"].empty?)
  address = "http://images.google.com/images?q="+
    cgi["query"].gsub(/ /,"+")+"&safe="+safe
  uri = URI(address)
  http = Net::HTTP.new(uri.host, uri.port)
  headers = {
    "User-Agent" => "Mozilla/4.0 (compatible; MSIE 5.5; Windows NT 5.0)"
    }

  code = http.head(address, headers).code.to_i
  if (code >= 200 && code < 300) then

      #the data is available...
      response = ""
      http.get(address, headers) do |chunk|
        response = response + chunk
      end
      images = []
      response.scan(
        /imgurl=(.+?)&imgrefurl=(.+?)&[^<]+<img src=(.+?) /
        ) do |img, ref, thumb|
        images<<[img,ref, thumb]
      end
      images.collect! do |e|
        "<a title='"+e[1]+" 'href='"+
        CGI.unescape(e[1]).gsub(/'/,"%27").gsub(/ /,"%20")+
        "' target='"+target+"'><img src='"+e[2]+"' height='100px'></a>"
      end
      cgi.out{
        images.join(" ")
      }
  end
end

I use a little php program to return a random line (initial search phrase/word) from a given file:


  $difficulty = 'easy';
  if (isset($_GET['difficulty'])&&strlen($_GET['difficulty'])>0) {
      $difficulty= $_GET['difficulty'];
  }
  $word_list = 'nouns.txt';
  $possible_word_lists = array('nouns.txt','wiki.txt');
  if (isset($_GET['word_list'])&&strlen($_GET['word_list'])>0) {
      if(in_array($_GET['word_list'],$possible_word_lists)){
        $word_list = $_GET['word_list'];
      }
  }


  $query = "";
  $word_count = 1;
  if(strcmp($word_list,'nouns.txt')==0){
    if(strcmp($difficulty,"medium")==0){
      $word_count = 2;
    }else if(strcmp($difficulty,"hard")==0){
      $word_count = 3;
    }
  }
  for($i=0; $i<$word_count; $i=$i+1){
    if(strlen($query)>0){
      $query= $query."+";
    }
    $query = $query.`ruby -e "a=File.readlines('$word_list').collect{|line| line.strip.gsub(' ','+').gsub('\'','%27')};puts a[rand*a.leng
    $query = trim($query);
  }
  print trim($query);

As you can see, I gave up on php half way through and cheated by calling a ruby one-liner. Someday I'll fix this up...