Sunday, December 6, 2009

Breathalyzer -- Facebook puzzle

More puzzles; in this post I provide the code for another puzzle provided on Facebook. Note that the unit test code is not included here. You can copy the code below after creating a project in an IDE such as Eclipse (which is what I use).

By the way, this puzzle was interesting. I did spend some time thinking of the algorithm to solve the problem but then started Googling for it. Found out about LevenshteinDistance which solves this problem nicely. Have to confess though that my thoughts on solving this problem were focused on using a tree structure. After finding the solution (and also the code for that) I gave up on that approach.

So given below is the problem and the code. The code contains two different functions for getting the Levenshtein Distance. Was having problems with the first function and hence included the second; now have kept both.

By the way see the example below. The word "tihs" has "ties" as the closest although in the context of the example sentence I was not seeing this until I used the program to clear my mental block. An example of computer not having to deal with the "mental block".


PROBLEM: (from Facebook page)

To safeguard against the dreaded phenomenon of wall posting while drunk, Facebook is implementing a feature that detects when post content is too garbled to have been done while sober and informs the user that they need to take an online breathalyzer test before being allowed to post.

Unfortunately, there is far too much content for a given set of persons to evaluate by hand. Fortunately, you are a programmer of some repute and can write a program that processes and evaluates wall posts. You realize that such a program would be of great use to society and intend to resolve the problem once and for all. The program you write must compute a score for a body of text, returning this score upon completion.

Your program will be given a list of accepted words and run on one wall post at a time. For each word W in the post, you must find word W' from the list of accepted words such that the number of changes from W to W' is minimized. It is possible that W is already W' and thus the number of changes necessary is zero. A change is defined as replacing a single letter with another letter, adding a letter in any position, or removing a letter from any position. The total score for the wall post is the minimum number of changes necessary to make all words in the post acceptable.


Input Specification

Your program must take a single string argument, representing the file name containing the wall post to analyze. In addition, your program must open up and read the accepted word list from the following static path location:
/var/tmp/twl06.txt
For testing purposes, you may download and examine the accepted word list here. When submitting your code, you do not need to include this file, as it is already present on the machine.

The input file consists entirely of lower case letters and space characters. You are guaranteed that the input file will start with a lower case letter, and that all words are separated by at least one space character. The file may or may not end with a new line character.

Example input file:
tihs sententcnes iss nout varrry goud
You are guaranteed that your program will run against well formed input files and that the accepted word list is identical to the one provided for testing.


Output Specification

Your program must print out the minimum number of changes necessary to turn all words in the input wall post into accepted words as defined by the word list file. Words may not be joined together, or separated into multiple words. A change in a word is defined as one of the following:
  1. Replacing any single letter with another letter.
  2. Adding a single letter in any position.
  3. Removing any single letter.
This score must be printed out as an integer and followed by a single new line.

Example Output (newline after number):
8




------------------------------------------------------------------------------------------

package com.farooq;

import java.io.BufferedReader;
import java.io.FileReader;
import java.util.ArrayList;

public class Word_distance {

/**
* @param args
*/
private ArrayList legal_word_list = new ArrayList();

public static void main(String[] args) {

String[] sentence_to_check = new String[args.length];
int Breath_analyzer_number; // the result we are looking for

//read the sentence which has to be tested from command line/file etc
if (args.length > 0) {
sentence_to_check = args;
}

//create an instance and determine the Levenshtein distance
Word_distance sentence_LD = new Word_distance();

Breath_analyzer_number = sentence_LD.sentenceAnalysis(sentence_to_check);
System.out.println("The number for the given sentence is " +Breath_analyzer_number);

}

public Word_distance() {
String filename = "C:\\tempfiles\\word_list_Levenshtein.txt";
String line_read;
try {
FileReader word_list_file = new FileReader(filename);
BufferedReader word_buffer = new BufferedReader(word_list_file);

while ((line_read = word_buffer.readLine()) != null) {
//so reading the list of "legal" words into the ArrayList
legal_word_list.add(line_read);
}
word_list_file.close();

} catch (Exception e) {
System.out.println("Exception is " +e);
}
}

public int sentenceAnalysis(String[] sentence) {

// get each word of sentence and determine Levenshtein number for word
//have to iterate the given word over entire legal_word_list
//and determine lowest value; if zero then break, repeat for all words
int total_distance =0;
int[] word_distance = new int[sentence.length] ; //minimum word_distance for each word in the sentence
int temp_distance;


for (int i=0;i< sentence.length; i++) {

for (int j=0; j< legal_word_list.size();j ++) {
temp_distance = getLevenshteinDistance(sentence[i], legal_word_list.get(j).toLowerCase());
// temp_distance = LD(sentence[i].trim(), legal_word_list.get(j).toLowerCase());

if (temp_distance ==0) {
word_distance[i] = temp_distance;
break;
} else if (j==0) {
word_distance[i] = temp_distance;
} else if (word_distance[i] > temp_distance) {
word_distance[i] = temp_distance;
} else {
//DO NOTHING as new word distance is larger than what we had
}
}
total_distance = total_distance + word_distance[i];
}
return total_distance;
}


// the following code copied from http://www.merriampark.com/ldjava.htm by Chas Emerick
public int getLevenshteinDistance (String s, String t) {
if (s == null || t == null) {
throw new IllegalArgumentException("Strings must not be null");
}

/*
The difference between this impl. and the previous is that, rather
than creating and retaining a matrix of size s.length()+1 by t.length()+1,
we maintain two single-dimensional arrays of length s.length()+1. The first, d,
is the 'current working' distance array that maintains the newest distance cost
counts as we iterate through the characters of String s. Each time we increment
the index of String t we are comparing, d is copied to p, the second int[]. Doing so
allows us to retain the previous cost counts as required by the algorithm (taking
the minimum of the cost count to the left, up one, and diagonally up and to the left
of the current cost count being calculated). (Note that the arrays aren't really
copied anymore, just switched...this is clearly much better than cloning an array
or doing a System.arraycopy() each time through the outer loop.)

Effectively, the difference between the two implementations is this one does not
cause an out of memory condition when calculating the LD over two very large strings.
*/

int n = s.length(); // length of s
int m = t.length(); // length of t

if (n == 0) {
return m;
} else if (m == 0) {
return n;
}

int p[] = new int[n+1]; //'previous' cost array, horizontally
int d[] = new int[n+1]; // cost array, horizontally
int _d[]; //placeholder to assist in swapping p and d

// indexes into strings s and t
int i; // iterates through s
int j; // iterates through t

char t_j; // jth character of t

int cost; // cost

for (i = 0; i<=n; i++) {
p[i] = i;
}

for (j = 1; j<=m; j++) {
t_j = t.charAt(j-1);
d[0] = j;

for (i=1; i<=n; i++) {
cost = s.charAt(i-1)==t_j ? 0 : 1;
// minimum of cell to the left+1, to the top+1, diagonally left and up +cost
d[i] = Math.min(Math.min(d[i-1]+1, p[i]+1), p[i-1]+cost);
}

// copy current distance counts to 'previous row' distance counts
_d = p;
p = d;
d = _d;
}

// our last action in the above loop was to switch d and p, so p now
// actually has the most recent cost counts
return p[n];
}


// following code also from web site; not my code
//*****************************
// Compute Levenshtein distance
//*****************************

public int LD (String s, String t) {
int d[][]; // matrix
int n; // length of s
int m; // length of t
int i; // iterates through s
int j; // iterates through t
char s_i; // ith character of s
char t_j; // jth character of t
int cost; // cost

// Step 1

n = s.length ();
m = t.length ();
if (n == 0) {
return m;
}
if (m == 0) {
return n;
}
d = new int[n+1][m+1];

// Step 2

for (i = 0; i <= n; i++) {
d[i][0] = i;
}

for (j = 0; j <= m; j++) {
d[0][j] = j;
}

// Step 3

for (i = 1; i <= n; i++) {

s_i = s.charAt (i - 1);

// Step 4

for (j = 1; j <= m; j++) {

t_j = t.charAt (j - 1);

// Step 5

if (s_i == t_j) {
cost = 0;
}
else {
cost = 1;
}

// Step 6

d[i][j] = Minimum (d[i-1][j]+1, d[i][j-1]+1, d[i-1][j-1] + cost);

}

}

// Step 7

return d[n][m];

}



//****************************
// Get minimum of three values
//****************************

private int Minimum (int a, int b, int c) {
int mi;

mi = a;
if (b < mi) {
mi = b;
}
if (c < mi) {
mi = c;
}
return mi;

}




}

Liar, Liar -- Facebook Puzzle

This was an interesting puzzle from Facebook. See the code below; i do not include unit test code here. You should be able to create a project in an IDE such as Eclipse (which is what I use) and create a class with the code below in the class. Similarly, you can use Eclipse to create the unit test code.

By the way, the code below is not completely robust though it works in several cases; a few more TODOs to be addressed that you see in the code. I got tired and so have put this code out without completing it.

For preciseness, the situation that the code does not address is when you lack commonality while processing each group of accuser and accused people leading to a disjoint group of liars and non-liars. The code works fine otherwise.

PUZZLE
As a newbie on a particular internet discussion board, you notice a distinct trend among its veteran members; everyone seems to be either unfailingly honest or compulsively deceptive. You decide to try to identify the members of the two groups, starting with the assumption that every senior member either never lies or never tells the truth. You compile as much data as possible, asking each person for a list of which people are liars. Since the people you are asking have been around on the board for a long time, you may assume that they have perfect knowledge of who is trustworthy and who is not. Each person will respond with a list of people that they accuse of being liars. Everyone on the board can see that you are a tremendous n00b, so they will grudgingly give you only partial lists of who the liars are. Of course these lists are not to be taken at face value because of all the lying going on.

You must write a program to determine, given all the information you've collected from the discussion board members, which members have the same attitude toward the telling the truth. It's a pretty popular discussion board, so your program will need to be able to process a large amount of data quickly and efficiently.

------------------------------------------------------------------------------------
CODE:

/**
* THIS PROGRAM SOLVES A PUZZLE THAT WAS GIVEN ON FACEBOOK; DETAILS BELOW.
*
* Your program must take a single command line argument; the name of a file. It must
* then open the file and read out the input data. The data begins with the number of
* veteran members n followed by a newline. It continues with n chunks of information,each
* defining the accusations made by a single member. Each chunk is formatted as follows:
*
* followed by m lines each containing the name of one member that the accuser says is a
* liar. accuser name and m are separated by some number of tabs and spaces. m will
* always be in [0, n]. All member names contain only alphabetic characters and are
* unique and case-sensitive.
*
*
*/
package com.farooq;

import java.io.BufferedReader;
import java.io.FileReader;
import java.util.ArrayList;

/**
* @author fanjum
*
*/
public class LiarDetection {
// a few TO DO's remain before this can be called robust code
//The basic algorithm works and has been tested.

/**
* @param args
*/

private ArrayList realGroupOne = new ArrayList();
private ArrayList realGroupOneOpposite = new ArrayList();
private ArrayList dummyGroupOne = new ArrayList();
private ArrayList dummyGroupOneOpposite = new ArrayList();

boolean doneWithAll; // to check if all people have been categorized
int lineNumber; // to maintain info about the number of lines read from file.
static String fileName; // the file which contains the detailed info
int numberOfPeople;
FileReader fileToRead; // file reader to be kept open until done
BufferedReader filebuffer;
int dummyCounter; // to split groups in the dummyGroup
final int DUMMY_INIT = 1000;

public static void main(String[] args) {

// get the file name;
fileName = "C:\\tempfiles\\file_liar_liar.txt";

// get the object initialization done
LiarDetection LD = new LiarDetection();

//now divide the people into two arrays associated with created LD object

LD.dividePeople();

System.out.println("WE ARE DONE");
//PRINT TWO REAL ARRAY LISTS IF YOU WANT and check.
for (int i=0;i System.out.println("next in this group is " +LD.realGroupOne.get(i));
}
for (int i=0; i < LD.realGroupOneOpposite.size(); i++) {
System.out.println("next in opposite groups i " +LD.realGroupOneOpposite.get(i));
}
}

public LiarDetection() {

String first_line, next_line;
String first_part, second_part; // to read the accuser name and number accused
int space_index; //to determine where accuser name ends and number accused begins
int numAccused; //to determine number accused
dummyCounter =DUMMY_INIT; // initializing dummyCounter to 1000;
//open the file and keep it open

try {
fileToRead = new FileReader(fileName);
filebuffer = new BufferedReader(fileToRead);

//read teh file and get the number numberOfPeople;

first_line = filebuffer.readLine();
numberOfPeople = Integer.valueOf(first_line).intValue();

// read the first block with member name, number accused m and accused names
lineNumber = 1; // to signify that one line was read
next_line = filebuffer.readLine().trim();
space_index = next_line.indexOf(" ");
first_part = next_line.substring(0, space_index); //name of accuser
second_part = next_line.substring(space_index).trim(); //number of accused in String
numAccused = Integer.valueOf(second_part).intValue(); //convert to int
realGroupOne.add(first_part);

for (int i=1; i<=numAccused; i++) {
//put all this in one of the two real ArrayLists;
//associate lineNumber with number of lines read so you know where to start next
next_line = filebuffer.readLine().trim();
realGroupOneOpposite.add(next_line);
}
} catch (Exception e) {
System.out.println("have an exception" +e);
}


}

public void dividePeople() {
// we start to divide people into two camps
// we start with 2 since we have read the accusation of first member
for (int mem_number=2; mem_number<=numberOfPeople; mem_number++) {
//make a call to see if we are done;
doneWithAll = checkIfDone();

if (!doneWithAll) {
//not divided all, so go to read accusation of mem_number
//A. read name of next member and the accusation list as String and String[]
//B. put the String and String[] in proper ArrayLists real or dummy and return here
//next method does both A and B above
divideNextGroup();
} else {
//we have divided all and so we are done
//close FILEREADER
try {
if (fileToRead != null)
fileToRead.close();
} catch (Exception e) {

}
break;
}
}


}
public boolean checkIfDone() {
// check if realGroupOne and realGroupOneOpposite have all n members with no dups
//if dups then a problem
//also the dummyGroupOne and its pair should have no members.
if (realGroupOne.size() + realGroupOneOpposite.size() == numberOfPeople) {
// we are done
return true;
} else {
return false;
}

}

public void divideNextGroup(){
String next_line;
String accuser_name, number_accused; // to read the accuser name and number accused
int numAccused; //number accused as an int
int space_index; //to determine where accuser name ends and number accused begins
String [] accused = new String[numberOfPeople];
boolean dividedIntoGroup; // true when the group members are put into one of four arrayLists.

try {
//read next member into String accuser_name
//read list of accused into String[] accused; accuser_name and accused go into different groups
next_line = filebuffer.readLine().trim();
space_index = next_line.indexOf(" ");
accuser_name = next_line.substring(0, space_index); //name of accuser
number_accused = next_line.substring(space_index).trim(); //number of accused in String
numAccused = Integer.valueOf(number_accused).intValue(); //convert to int
for (int i=0;i accused[i] = filebuffer.readLine().trim();
}

//check if accuser in realGroupOne or realGroupOneOpposite and if so
// ensure that all accused are in the other group; ensure no duplicates while adding.
dividedIntoGroup = isGroupInRealList(accuser_name, accused,numAccused);

if (!dividedIntoGroup) {
// not divided into realList, so check dummyList
dividedIntoGroup = isGroupInDummyList(accuser_name, accused,numAccused);

if (!dividedIntoGroup) {
//still not divided into group, so we put this in the dummyLists separated by an ID
putGroupInDummyList(accuser_name, accused, numAccused);
}
}

}catch (Exception e){

}

// when here the group has been divided in either the real group or the dummy group; so done
}

public boolean isGroupInRealList(String accuserName, String[] accusedList, int numberAccused) {

boolean inList = false;

// 6. check if A is IN any of the two real ArrayLists;
// 7a. If A in any of two real ArrayLists, add (B,C,...N) to the paired AL GO TO 4A.
// 7b. If A in none of two real ArrayLists, GO TO 8.
// 8. check if any of (B,C,..N) is in any of two real ArrayLists.
// 9a. If (B,C,..N) in any of two real ArrayLists add others to same AL,
// add A to paired AL, GO TO 4A.

// ALSO CHECK FOR INCONSISTENCY AND THROW AN EXCEPTION OR WHATEVER:
if (realGroupOne.contains(accuserName)) {
// accuser is in GroupOne, so add all in accusedList to other without dups
inList = true;
for (int i=0;i if (realGroupOne.contains(accusedList[i])) {
//this is a problem, should not happen
}
if (realGroupOneOpposite.contains(accusedList[i])) {
//accused name is already in list and so go to next
continue;
} else {
realGroupOneOpposite.add(accusedList[i]);
}
}

} else if (realGroupOneOpposite.contains(accuserName)) {
//accuser is in GroupOneOpposite so add all in accusedList to other without dups
inList = true;
for (int i=0;i if (realGroupOneOpposite.contains(accusedList[i])) {
//this is a problem, should not happen
}
if (realGroupOne.contains(accusedList[i])) {
//accused name is already in list and so go to next

} else {
realGroupOne.add(accusedList[i]);
}
}

} else {

//so accuser is not in either of real lists; so check if accused names are in either
for (int i=0;i //check if each accused in either of real lists
if (realGroupOne.contains(accusedList[i])) {
//add all the rest of the accusedList to GroupOne without duplicates
//and add accuser to other group
inList = true;
for (int j=i+1;j //add rest of group to GroupOne
if (!(realGroupOne.contains(accusedList[j]))) {
//add to this group
realGroupOne.add(accusedList[j]);
}
}
realGroupOneOpposite.add(accuserName);

break;
}
if (realGroupOneOpposite.contains(accusedList[i])) {
//add all the rest of the accusedList to GroupOne without duplicates
//and add accuser to other group
inList = true;
for (int j=i+1;j //add rest of group to GroupOne without duplicates
if (!(realGroupOneOpposite.contains(accusedList[j]))) {
//add to this group
realGroupOneOpposite.add(accusedList[j]);
}
}
realGroupOne.add(accuserName);

break;

}

}

}
return inList;
}

public boolean isGroupInDummyList(String accuserName, String[] accusedList, int numberAccused) {
int indexOfName, indexOfCounter;
int tempindex;
boolean indummylist = false;
// 10. check if A in any of two dummy ALs.
// 10a. If A in any of two dummy ALs, add (B, C,...N) to same block in paired dummy AL.
// GO TO 4A.
// 10b. check if any of B,C, ...N in any of two dummy ALs.
// 10c. Merge all the blocks that contain any of B,C, ...N into one TEMPBLOCK with unique elements.
// also remove the blocks when you put them into TEMPBLOCK;
// 10d. for every block that contains B,C,..N, merge the corresponding block in other
// AL into one TEMPBLOCK with unique elements.
// 10e. get teh next counter, add to dummy AL and merge TEMPBLOCK back into dummy AL.
// each TEMPBLOCK should be in a different dummy AL.

if (dummyGroupOne.contains(accuserName)) {
// accusername is in this; find index and add to same subGroup in other dummy group
indexOfName = dummyGroupOne.indexOf(accuserName);
indummylist = true;
for (int j=indexOfName; j >=0; j--) {
// to go and figure out the counter index
try {
indexOfCounter = Integer.valueOf(dummyGroupOne.get(j)).intValue();
//so now indexOfCounter contains the counter number
tempindex = dummyGroupOneOpposite.indexOf(Integer.toString(indexOfCounter));

for (int i=0; i //have to check if the ith accused is in dummy list but we skip that now
//TO DO --- SO THIS IS TO BE FIXED
dummyGroupOneOpposite.add(tempindex+1,accusedList[i]);
}
} catch(NumberFormatException e) {
continue;
}
}

} else if (dummyGroupOneOpposite.contains(accuserName)) {
//accuserName is in this; find index and add to same subGroup in other dummy group
indexOfName = dummyGroupOneOpposite.indexOf(accuserName);
indummylist = true;
for (int j=indexOfName; j >=0; j--) {
// to go and figure out the counter index
try {
indexOfCounter = Integer.valueOf(dummyGroupOneOpposite.get(j)).intValue();
//so now indexOfCounter contains the counter number
tempindex = dummyGroupOne.indexOf(Integer.toString(indexOfCounter));

for (int i=0; i //have to check if the ith accused is in dummy list but we skip that now
//TO DO --- SO THIS IS TO BE FIXED
dummyGroupOne.add(tempindex+1,accusedList[i]);
}
} catch(NumberFormatException e) {
continue;
}
}
}

//now check if accused[] are in either of the groups, if so add accuser to other dummy group
//also ensure rest of accused[] are in the same subgroup

//TO DO -- check on this and complete this.


// TO DO -- ALSO MERGE REAL AND DUMMY LISTS;
return indummylist;
}

public void putGroupInDummyList(String accuserName, String[] accusedList, int numberAccused) {
// 11. If (B,C,..N) in none of four ALs add A to dummyAL_G1 and (B,C,..N)
// to dummyAL_G2 after counter

//adding accuser to dummy Group One after counter
dummyGroupOne.add(Integer.toString(dummyCounter));
dummyGroupOne.add(accuserName);

//adding accused to dummy Group OneOpposite after counter
dummyGroupOneOpposite.add(Integer.toString(dummyCounter));
for (int i=0; i< numberAccused; i++) {
dummyGroupOneOpposite.add(accusedList[i]);
}
dummyCounter++;

}
}

Puzzles in Facebook

I came across this page on Facebook's site. The page provides different puzzles with increasing level of complexity. Each puzzle requires both algorithmic as well as programming experience.

Planning to solve these to get me back in my programming groove. Over this weekend I was able to solve 2 puzzles with a "snack" level of difficulty completely. Have the algorithm worked out for a third and need to code it.

Now I plan to put the code up on the blog. I also plan to take these as examples to post on Google's app-engine and also address scalability by leveraging concepts such as MapReduce, BigTable. But all this planned for the future. Let us see how much I can get done in the "infinite" amount of free time that I have.

Friday, November 27, 2009

Workforce telepathy

Nice to see some interest in how to enable a workforce to be more efficient; GigaOm has an article on a stealth startup in this area Asana .

To quote
While intentionally vague about how the Asana software will work, the two are very clear as to what they want it to do: help people collaborate and better manage their time in an office (and eventually home) environment, where there are a lot of tasks and too much information coming at workers. Put another way, they want it to enable everyone on a team to read each other’s minds and act accordingly.
From an email attributed to Rosenstein: --And still it felt like not a day went by that there wasn’t some miscommunication or misunderstanding that slowed down the project just a little, a death by a thousand cuts. You see it in every organization. One person will think that X is the most important thing they could be working on, but if they’d talked to their manager they would have gotten a different answer, and their teammates might think it was a third thing. These are all problems of information transparency. This problem has only gotten worse with the explosion of information that knowledge workers need to manage, often coming at them from a variety of tools and inboxes, none of which work together.

Both Rosenstein and Moskovitz (a Facebook co-founder) admit this is a hard problem, which is why they’ve started small with a five-person team (one’s an admin) and lots of brand-name Silicon Valley advisers. The most recent funding, which comes on top of $1.2 million raised from angels including Facebook investor Peter Thiel, super angel Ron Conway, Lotus developer Mitch Kapor and Napster founder Sean Parker, could help the company hire more people to build out this magical software. But Asana doesn’t plan on hiring too many people.

While the founders assure me that Asana’s product is completely different from anything on the market today, it comes across as mishmash of Yammer, Basecamp and Google Wave.


Saturday, November 7, 2009

Commoditization of cellular networks

We all have heard about how the wireless providers do not their networks to be dumb pipes. But developments lately seem to be moving towards making the cellular networks as dumb pipes. Interesting article in Forbes where the author points out how this is happening with cooperation of small operators such as MetroPCS.

and this might be better for the consumers. Of course, network providers can also provide services on such dumb networks and if these services are compelling, they can still make money.

A decade ago there were three phone businesses: local, long distance and cellular. The first two have already collapsed, done in by advancing Internet and cellular technology and the cutthroat competition they unleashed. Americans paid $110 billion annually for long-distance phone calls nine years ago. It's now down to $55 billion and still shrinking. Local phone companies took in $126 billion at its peak eight years ago; that sum has fallen to $86 billion and is dropping fast.

To date the cellular calling industry has been immune from the commoditization infecting the rest of the phone business. Today's Big Four carry more phone calls than ever (almost 2 trillion minutes last year) and took in more money doing it than ever before ($105 billion). Collectively they control 90% of the U.S. market, and this cozy oligopoly hasn't succumbed to ruinous price wars--yet. Over the past three years, for instance, the four giants hiked the price of single text messages from 10 cents to 15 cents, and then to 20 cents, despite the lack of any plausible link to their underlying costs.
When Linquist looks at that sort of pricing he sees not strength but weakness. Modern cell phones can do thousands of things, from downloading TV shows to finding the nearest Korean restaurant. Nevertheless, the cellular industry still makes almost all its money charging for just two applications: making phone calls ($116 billion) and sending text messages (roughly $12 billion, the carriers won't give exact figures). Everything else is considered generic "data." All of those thousands of other uses, many of which put much greater strain on the network than calling or texting, bring in the remaining $20 billion in revenue.

Apple first loosened the industry's hold two years ago when it persuaded AT&T to let iPhone owners download all sorts of software enhancements. AT&T consented, but didn't give up full control, requiring the software be screened before it went on sale. It banned applications that let customers make phone calls using their cellular Internet connection, and for an obvious reason: In both the local and long distance phone markets, customers' ability to route calls over the Internet helped new competitors savage prices.'

In October AT&T caved in to consumer and political pressure and lifted its blockade of Internet calling software. The market chopped 2% off AT&T shares the next day, but Ma Bell's competitors followed suit anyway. Verizon announced it will release new phones that its subscribers will be able to customize in almost any way they please. Vonage, which specializes in shifting calls from traditional home phones to the Internet, unveiled similar software for the cellular world that can run on a BlackBerry.
and a few graphs in the article are also good. one of the graph shows how the average number of mins spent by a subscriber has gone up as the cost of each minute has come down although now it is reaching a plateau arguing for other services. The other graph shows how the revenue from local and long distance has come down over time. Cellular revenue is at an all time high now but for how long?


Friday, October 30, 2009

Software build tools

A tool that i want to explore is TeamCity -- a tool to enable continuous integration. A professional edition is available free of charge.

A tool for the purpose of code review is CodeCollaborator -- a tool for peer code review. another similar tool is Crucible.

Of course code coverage to be verified using Clover.

Tuesday, October 27, 2009

QA automation tools

Long time since I posted; have been busy with the launch of the platform that will support the Personal TV devices. This is a new device to watch live mobile TV launched by FLO TV. The platform launch happened on 9/9/9-- an easy to remember date.

Anyhow digressing from the main topic of this article. Here I wanted to comment on some of the automation tools available for QA testing. The ones i have in mind are Selenium, DeviceAnyWhere, JUnit and JMeter. I plan to comment briefly on each of these in future.

Today, I got to go since I need to put the project plan together for the next release of the Project Plan together. Several ongoing discussions about the scope and design of features.

Sunday, August 16, 2009

Future of blogging

An interesting article that I read at GigaOm (no link -- too lazy now). This is about how to make blogging more social. Why is it that I have to log in to the blogger network to post articles? Why can i not do it more seamlessly? And why is it that comments can only appear after the article? Why can the comments not be jived into the article depending on the quality of the comment?

Several challenges in making the above happen. A first step though is posterous. As I understand it it makes blogging seamless whereby i could post anything (photos, videos, files etc) to an email address. and they set up a page with the content and reply back. Got to check it out. This might address one small part of the first wish (post seamlessly) in the paragraph above.

So went ahead and tried it. A problem though is that I cannot choose the URL that I want. they do provide a URL yourname.posterous but they seem to append random characters. Something that I do not like and not only that. Makes it difficult for me to remember the web site corresponding to me. Would have liked a choice here if "myname.posterous" is taken.

Of course, to get over the above problem, i got to log in and sign in. Hmm, they should have made this clear up front.

Future of Work:

The title here is the same title in a blog post on the gigaom network. This post talks about the formation of groups to execute projects in the future. And what is interesting is how the group dynamics would help the execution. We need a way to help determine the group dynamics before a group is far down the road; in other words, a tool to help people determine if they can work easily with each other and avoid people who do not share chemistry.

Sunday, July 19, 2009

Cargo cult management

Recently learned of a nice way to describe some of the things we see happening at work; people carrying out actions hoping that these will lead to desired outcomes; confusing the necessary condition for a sufficient condition. The term for this "cargo cult management. Search on wikipedia for details.

Innovating through recession

Recently had a chance to attend a talk by AndrewR on this topic. A white paper from the same author is here.

OpenSocial

Need to investigate OpenSocial; the link here seems to be a good place to start. On my todo list.

Editor for Python scripts

Recently have started on Python; using this to do some analysis of the integration and system testing process. A problem that I see here though is with all the indentation rules that Python has especially with respect to the various looping and conditional constructs.

By the way I was using Notepad for writing the scripts; have now downloaded Notepad++. Getting used to this new editor. Why did i select this; well, there were several editors and I just picked the one based on the features.



Bad behavior at work

One of the things I have been thinking of is the cost of incompatibility at the workplace.

Came across this book "The Cost of Bad Behavior: How Incivility is Damaging Your Business and What to Do About It By Christine Pearson and Christine Porath" . Maybe something to read at some point in the future. I wonder what the authors point of view on this is.

The following is a brief review of the book from cnn

The Talmud says that embarrassing people in public is like spilling their blood, yet it's one of the many forms of emotional homicide that are common in the workplace.

The authors want the killing to stop. They make an earnest case that incivility on the job can wound your company by demoralizing workers and driving off customers. The book even offers a worksheet to help you put a dollar value on the problem.

Don't bother. Workplace nastiness can't do anyone much good, including business owners. Take Pearson and Porath's sensible advice for heading it off and rooting it out. Doing so will make you richer or, at the very least, let you sleep the sleep of the just.

Friday, July 10, 2009

Architecture for LinkedIn

Was recently wondering about the architecture of LinkedIn. I guess the current atmosphere with a nearly 10% unemployment rate made me think about it.

More details of the architecture are described in this blog. Links to two presentations also. Thinking of putting my programming skills to the test by creating something similar.

Sunday, July 5, 2009

Open source resources

A nice article on open source resources.

Sticky ideas

Communication is a very important tool of a successful systems engineer. This is because a systems engineer has to be able to convey his/her ideas to several teams (Prod Mgt, Developers, testers, marketing etc).

Along these lines, recently read a cool book on effective communications. The gist of the book is very nicely summarized here. By the way, the book is available here.

The "stickiness factor" is one of the three rule of epidemics described in The Tipping Point. The other two being "The law of the few" and "The power of context".

MySQL password reset

Had a problem; I wanted to write some Java code leveraging the MySQL installation. But before I did that I just wanted to use MySQL directly.

With this in mind, I tried to fire up MySQL command line client but viola I had forgotten the password. Try as I might, I could not get the right password.

Time to do something. Search the "internets" to figure out how to reset the password. It was not as difficult as I thought it might be. Details given here made it a cinch.

And so now I have a new password which is written down and not just saved in my brain.

By the way, there seems to be good documentation on MySQL here. Also some good hints about database backups here; have to check these out.

Friday, July 3, 2009

Linux on a Windows machine

Recently had a chance to install Wubi. It makes for such easy installation of ubuntu on a Windows machine.

From Wikipedia "Wubi (Windows-based Ubuntu Installer) is an official Windows-based free software installer for Ubuntu."

A problem though is that I need to reboot the machine to access Ubuntu. And that means all the windows I have open need to be shut down. Virtualization is the answer I guess.

Sunday, June 28, 2009

Bugzilla installation

I have lately been working on putting together the various components that are used in the different phases of a software life cycle. Why do this?

This was spurred by my current project where I am leading a team working on implementing the platform needed for realizing mobile TV. This based on Qualcomm's MediaFlo technology. The problems with some of the components used here was the catalyst.

We were using the Clearquest tool to log software bugs but there were several issues with that. That spurred my decision to install and deploy Bugzilla. Details of this installation are given for a Windows XP machine here. As part of this I also downloaded MySQL, Perl and Apache on my machine. A great description.

Good use of a Sunday.

Wednesday, January 7, 2009

Emergency

Have a personal emergency and hence have not been able to post for a long time. Difficult times and hope to emerge stronger from this.