About this user

Guido Sohne http://sohne.net

« Newer Snippets
Older Snippets »
6 total  XML / RSS feed 

Convert numbers to words

Ugly code. But it works. If you have something better, then post it.

class Number
  
  def self.to_words(number)
    Number.new.to_s(number)
  end
  
  def self.commify(number)
    (s=number.to_s;x=s.length;s).rjust(x+(3-(x%3))).gsub(/(\d)(?=\d{3}+(\.\d*)?$)/,'\1,')
  end
    
  def initialize
    @unit = %w[zero one two three four five six seven eight nine]
    @teen = %w[ten eleven twelve thirteen fourteen fifteen sixteen seventeen eighteen nineteen]
    @tens = %w[zero ten twenty thirty fourty fifty sixty seventy eighty ninety]
    @qtys = %w[hundred thousand million billion trillion quadrillion quintillion]
    @zero = ["zero"]
    @hundred = "hundred"
    @sepr = "and"
  end
    
  def to_s(number)
    out = quantify(number).flatten
    for x in 0 .. out.length - 1
      out[x] = nil if out[x] == @sepr && out[x+1] == @sepr
    end
    out.compact!
    out = @zero if out.length == 1 && out[0] == @sepr
    out.pop while out.last == @sepr
    out.shift while out.first == @sepr    
    out.join(' ').gsub(/ ,/,',')
  end
        
  private 
  
  def padded_groups(v)
    out = []
    padded = (s=v.to_s;x=s.length;s).rjust(x+(3-(x%3))).gsub(//,'0')
    padded.scan(/.{3}/)
  end
  
  def wordify(v)
    out = []
    zero = '0'[0]
    h, t, u = v[0] - zero, v[1] - zero, v[2] - zero
    if h != 0
      out << @unit[h]
      out << @hundred
    end
    out << @sepr if h != 0 && (t != 0 || u != 0)
    out << @sepr if h == 0 && t == 0 && u != 0
    if t == 1
      out << @teen[u]
    else
      out << @tens[t] if t != 0
      out << @unit[u] if u != 0
    end
    return out
  end
  
  def quantify(v)
    v = padded_groups(v).reverse
    pos = v.length - 1
    out = []
    while pos >= 0
      word = wordify(v[pos])
      if word[0] != nil
        out << word
        out << @qtys[pos] if pos != 0                
      else
        out << @sepr
      end
      pos -= 1
    end
    return out
  end

end

# for number in [
#     0, 
#     1, 
#     3, 
#     11, 
#     100,
#     1000, 
#     1001, 
#     1100, 
#     1101, 
#     1_000_001, 
#     8_000_000_000, 
#     8_000_000_001, 
#     4_567_890_923, 
#     6_804_567_890_903, 
#     5_006_804_567_890_903
#   ]
#   print "#{Number.commify(number)}: #{Number.to_words(number)}\n"
# end

Convenient and fast text input using a numeric keypad

Recently, I had to program a device that had only a numeric keypad for some light data entry. The manufacturer had a relatively cumbersome method for entering text and I had to come up with an alternative means to more conveniently enter text using the numeric keypad.

The application did not require mixed case characters, so I decided to limit data entry to uppercase characters to reduce the number of keystrokes required. Unlike typical mobile phone input, there is no timeout for the next character to be entered. Adding this is not hard at all, and would probably only require about three to five extra lines of code ...

void succ(int* ip, char* cp, char* ch)
{
    int il = strlen(ch);
    char *np;
        
    if(*(cp + *ip) == '\0') {
        *(cp + *ip) = *ch;
        return;
    }
        
    np = strchr(ch, *(cp + *ip));
        
    if(np == NULL) {
        (*ip)++;
        *(cp + *ip) = *ch;
    } else if(np == ch + il - 1) {
        *(cp + *ip) = *ch;
    } else {
        *(cp + *ip) = *(np + 1);
    }
}

int textual_input(int line, char *destination, int maxlen)
{
    int nKey, i = 0;
    char text[128];

    memset(text, 0, sizeof(text));

    while(1)
    {
        nKey = Sys_Key_WaitKey();
        switch (nKey)
        {
            case KEY_DEL: text[i] = '\0'; i > 0 ? i-- : i = 0; break;
            case KEY_0: i++; text[i] = ' '; break;
            case KEY_1: i++; break;
            case KEY_2: succ(&i, text, "ABC"); break;
            case KEY_3: succ(&i, text, "DEF"); break;
            case KEY_4: succ(&i, text, "GHI"); break;
            case KEY_5: succ(&i, text, "JKL"); break;
            case KEY_6: succ(&i, text, "MNO"); break;
            case KEY_7: succ(&i, text, "PQRS"); break;
            case KEY_8: succ(&i, text, "TUV"); break;
            case KEY_9: succ(&i, text, "WXYZ"); break;
            case KEY_ENTER: goto done; break;
            case KEY_CANCEL: goto cancel; break;
        }
        Syd_ClearWindow(line,line);
        Syd_DisplayLine(line, text, 0, ALIGN_LEFT );
    }
done:
    strncpy(destination, text, maxlen);
    return 0;
cancel:
    memset(destination, 0, maxlen);
    return -1;
}


I hope someone finds this code useful. It's not a huge piece of code, not hard to understand and is also small enough to maintain. As always, I love to hear of better ways to do things so any suggestions are welcome!

Building a uClinux toolchain on Mac OS X (10.4.4)

[Article originally posted at http://sohne.net/articles/2006/02/13/building-a-motorola-coldfire-uclinux-toolchain-for-mac-os-x]

GETTING SOURCES

You'll need Bernardo Innocenti's sources available from

http://www.develer.com/uclinux/

It's probably wise to choose the most recent version of his sources, and hope for the best. Stick to the exact version of each source required (e.g. gcc 3.4.0 != gcc 3.4.3). I tried 3.4.3 but it crashed inside xgcc due to my incompetence at patching the sources.


BUILDING YOUR OWN

Your experience may differ from mine. What I do know is that building this takes quite some time and you won't enjoy doing it again and again to fix problems (unless that's your sort of fun!)

Before building, install DarwinPorts and
        sudo port install coreutils
        sudo port install gsed
        
        sudo ln -s /opt/local/bin/gcp /opt/local/bin/cp
        sudo ln -s /opt/local/bin/gnused /opt/local/bin/sed
        
        export PATH=/opt/local/bin:$PATH 
        
        sudo gcc_select 3.3

Start the build process by editing the build-uclinux-tools.sh script as per your needs. I used Apple gcc 3.3 with the following settings in build-uclinux-tools.sh ...
        TARGET=m68k-elf
        PREFIX=/usr/local/m68k-uclinux-tools
        BINUTILSVERS="2.14.90.0.8"
        GCCVERS="3.4.0"
        GDBVERS="6.1"
        BDMVERS="1.3.0"
        UCLIBCVERS="0.9.26"
        ELF2FLTVERS="20040326"
        ROOTDIR="${BASEDIR}/uClinux-dist"
        KERNEL="${ROOTDIR}/linux-2.4.x"

After setting up the build-uclinux-tools.sh script and downloading the necessary additional files and patches, you can do
        cd where-ever-your-buildroot-is
        sh build-uclinux-tools build

and each time you encounter an error (and hopefully fix what's causing it) you can do
        sh build-uclinux-tools continue


BUILD ERRORS (AND SOLUTIONS)

You're going to run into various errors during the build. These steps document how I managed to get past them. There are probably mistakes in them, or at least better ways to do them. Suggestions and improvements are most welcome. It should be easy to make this into a patch. Maybe I'll do that the next time around, but it took so long to build that I wouldn't hold my breath for it.


1) Building elf2flt-20040326

You'll get an error saying "elf.h not found" (it's not present on Darwin systems)
        cp uClibc-0.9.26/include/elf.h elf2flt-20040326

Edit elf2flt-20040326/elf.h and uncomment the #include line. During linking, you'll get an error saying crt0.a not found. Edit Makefile.in, and change
                LDFLAGS = @LDFLAGS@ -static

to
                LDFLAGS = @LDFLAGS@ 

And that should take care of it for you. Continue building via
        sh build-uclinux-tools continue    


2) Building m68k-bdm-1.3.0

This will fail because BDM hardware access hasn't been written for Darwin yet. The ioperm system call is missing, and there's no native driver, so edit build-uclinux.tools.sh and in the stageA function, replace the following lines

        --with-libiberty=${BASEDIR}/${TARGET}-binutils/libiberty/libiberty.a \
        ${PREFIXOPT}

with the lines below

     --with-libiberty=${BASEDIR}/${TARGET}-binutils/libiberty/libiberty.a \
        --disable-ioperm --disable-driver \
        ${PREFIXOPT}

i.e. add --disable-ioperm and --disable-driver to the configure stanza.


3) Building m68k-bdm-1.3.0

This will fail again since its trying to build a static executable. Apparently, that's a no-no on Darwin. Edit Makefile.in and Makefile.am in the utils directory and remove the -static lurking as below:-
        bdmctrl_LDFLAGS = -static

should be changed to
        bdmctrl_LDFLAGS =

You should be still able to connect to a remote server that's running BDM. Since I'm running on a Powerbook, which doesn't even have a parallel port, it's not a big deal to not build the hardware interface bits of BDM. Your mileage may vary. You should be able to continue your build now.


4) Building gdb-6.1

You will receive an error due to an incorrect gdb.texinfo file. Edit gdb-6.1/gdb/doc/gdb.texinfo and around line 6961 (or whatever makeinfo complained about) and change
@strong{Note:} a trace experiment and data collection may stop automatically if any tracepoint's passcount is reached
(@pxref{Tracepoint Passcounts}), or if the trace buffer becomes full.

to become
Note: a trace experiment and data collection may stop automatically if any tracepoint's passcount is reached
(@pxref{Tracepoint Passcounts}), or if the trace buffer becomes full.

After this, you should have a successful build. All the tools will be installed on your system by Bernardo's excellent build script.

Format an integer with commas to make it more readable

More information or better ways of doing this welcome ...

def commify(number)
    c = { :value => "", :length => 0 }
    r = number.to_s.reverse.split("").inject(c) do |t, e|  
      iv, il = t[:value], t[:length]
      iv += ',' if il % 3 == 0 && il != 0    
      { :value => iv + e, :length => il + 1 }
    end
    r[:value].reverse!
  end


Alex Young suggested

def commify(v)
 (s=v.to_s;x=s.length;s).rjust(x+(3-(x%3))).scan(/.{3}/).join(',').strip
end


and there's always

number_with_delimiter(number, delimiter)


from NumberHelper in the Rails API ... which is implemented as follows:-

def number_with_delimiter(number, delimiter=",")
  number.to_s.gsub(/(\d)(?=(\d\d\d)+(?!\d))/, "\\1#{delimiter}")
end


They don't always work too well when the number has a decimal point in it (and more than two or so digits after the decimal point), so there's room for an improved version ...

JV suggested the following snippet from http://pleac.sourceforge.net/pleac_ruby/numbers.html which appears to be able to handle commas and decimal places as well.

def commify(n)
    n.to_s =~ /([^\.]*)(\..*)?/
    int, dec = $1.reverse, $2 ? $2 : ""
    while int.gsub!(/(,|\.|^)(\d{3})(\d)/, '\1\2,\3')
    end
    int.reverse + dec
end

Rake task to load fixtures in a specific order

The default task for loading fixtures, rake load_fixtures, appears to load the fixture data in alphabetical order. If you have foreign key or other constraints, it may be impossible to load the fixtures into the database.

The rake task below searches for a file called test/ordered_fixtures.rb and loads that file. The file contains code that sets an environment variable specifying the load order for fixtures.

ENV["FIXTURE_ORDER"] = 
  %w( entities interaction_sources interaction_devices payments 
      interactions accounts employments enrollments payables 
      receivables tenures wards ).join(' ')


The lib/tasks/fixtures.rake (rake load_fixtures_ordered) task first loads all fixtures specified via ordered_fixtures, and then loads all other fixtures not specified as being ordered so it should work for you even if you do not create an ordered_fixtures file.

require File.expand_path(File.dirname(__FILE__) + "/../../config/environment")
require File.expand_path(File.dirname(__FILE__) + "/../../test/ordered_fixtures")

ENV["FIXTURE_ORDER"] ||= ""

desc "Load fixtures into #{ENV['RAILS_ENV']} database"
task :load_fixtures_ordered => :environment do
  require 'active_record/fixtures'  
  ordered_fixtures = Hash.new
  ENV["FIXTURE_ORDER"].split.each { |fx| ordered_fixtures[fx] = nil }
  other_fixtures = Dir.glob(File.join(RAILS_ROOT, 'test', 'fixtures', '*.{yml,csv}')).collect { |file| File.basename(file, '.*') }.reject {|fx| ordered_fixtures.key? fx }
  ActiveRecord::Base.establish_connection(ENV['RAILS_ENV'])
  (ordered_fixtures.keys + other_fixtures).each do |fixture|
    Fixtures.create_fixtures('test/fixtures',  fixture)
  end unless :environment == 'production' 
  # You really don't want to load your *fixtures* 
  # into your production database, do you?  
end


The code may not be the best but it Works For Me.

svnserve launchd item for OS X 10.4

It's not that straightforward getting svnserve to work under launchd. I can't take the credit for this but I thought it would be useful to have it here on TextSnippets.

1) Install subversion if you don't have it already. Here's how with DarwinPorts.

sudo port install subversion


2) Create your Subversion repository (for my own use, I used /Users/xyz/Repositories) e.g.

svnadmin create /Users/xyz/Repositories


3) Place the following XML into a file named org.tigris.subversion.svnserve in the /Library/LaunchDaemons directory.

<?xml version="1.0" encoding="UTF-8"?>
DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
        <key>Debugkey>
        />
        <key>GroupNamekey>
        xyz</string>
        <key>Labelkey>
        org.tigris.subversion.svnserve</string>
        <key>OnDemandkey>
        />
        <key>Programkey>
        /opt/local/bin/svnservestring>
        ProgramArguments</key>
        <array>
                <string>svnservestring>
                --inetd</string>
                <string>--root=/Users/xyz/Repositoriesstring>
        </array>
        <key>ServiceDescriptionkey>
        SVN Version Control System</string>
        <key>Socketskey>
        
                Listeners</key>
                <dict>
                        <key>SockFamilykey>
                        IPv4</string>
                        <key>SockServiceNamekey>
                        svn</string>
                        <key>SockTypekey>
                        stream</string>
                dict>
        </dict>
        <key>Umaskkey>
        2</integer>
        <key>UserNamekey>
        xyz</string>
        <key>inetdCompatibilitykey>
        
                Wait</key>
                <false/>
        dict>
</dict>
plist>


You'll need to change xyz to your user name ...

4) Test it out by doing a

sudo launchctl load /Library/LaunchDaemons/org.tigris.subversion.svnserve

sudo launchctl start /Library/LaunchDaemons/org.tigris.subversion.svnserve

svn co svn://your.host.name/aModule/In/Your/Repository
« Newer Snippets
Older Snippets »
6 total  XML / RSS feed