Sunday, December 18, 2011

Playing with Fire for a month.

Now that I have owned  Kindle Fire for a month, it's time to  look back and document my own and only my own opinion about it. This disclaimer is necessary to not upset ipad fan club. This is not meant to compare Fire with any other mobile device in the market. To surmise my findings in one sentence - it's not very often that you get more than what you paid for. That's Kindle Fire for you.  How much more? It's up to every one to decide after reading this post. In my opinion Fire hits that sweet spot of providing you with adequate features with superior performance at a very very affordable price. It's no wonder Fire is flying off the shelf since its launch.
For those with little patience for reading, the summary of my evaluation is presented here in a nice tabular format.




I received the Fire in my mail after a month long wait time. I had placed an order on Amazon, immediately after the product announcement basically trusting the good reviews from analysts. Right from the time I opened the box I was hooked. Fire comes in a cardboard box with perforated edge that tears without help from scissors, there are no sticky tapes any where outside or inside the box. I was off and running in less than 5 minutes. The interface is intuitive and easy. It has its own quirks, but they don't  rob you of your quality time.

First off, let's get out of our way some well documented and commented on 'lack of features of Fire'. They are, absence of any buttons other than power button. Not having  a button even for controlling volume is kind of annoying. The power button is at the bottom together with micro USB  port and a jack for headphones. Because of the unusual placement of power button, it's possible that Fire goes to sleep by holding it in your lap. But bringing it back to life is easy enough - sliding the switch on the screen like an apple device. The Fire is not exactly light and because of its very thin edges it's hard to hold it in a hand for a long period of time without touching the screen and without slipping through your hands. I would recommend Amazon to provide grooves or impressions/dents on the edges to make it easier to hold.
Fire does not have a microphone, neither does it have a camera. So for all those skype fans this could be a serious issue.

Now on to the best part.
I have used the Fire so far to do almost all my online activities like reading my emails from various personal accounts, using social networks like facebook, twitter, linkedin, watching videos on youtube, carrying out banking, investing functions, listening to local radio channels and Pandora, downloading books and apps from Amazon, watching HD quality movies from Amazon, sending documents to kindle over an email. Without a doubt,  I am extremely happy and pleasantly surprised at its ease of use. The Fire boots remarkably fast, downloads web content at great speed, shows crystal clear images and videos. The screen rolls at a very fast pace showing the power of dual core processor, virtual keyboard is sensitive and fun to operate.The sound quality is acceptable and volume is loud enough for a noisy room. Streaming videos of movies and TV episodes works super fast and quality of the display is awesome.

Now for the things I wish the Fire had -. I watch educational videos on you tube all the time. I really wanted  to download them to Fire, so I could watch them when I want and where I want, which according to my knowledge is not possible today. The Silk browser does not have any plug-ins that could perform this task. So you are back to downloading videos to your pc and then uploading them to Fire.
Another missing feature is the absence of folder structure. The content is automatically classified and stored in different sections like video, audio, books, magazines etc, but you can't access those from any other section that the menu at the top. The folder structure like windows explorer doesn't exist in Fire today. This prevents you from organizing your content in your own way.

Now for the quirks I found a little bit annoying. When you are watching youtube video in full screen mode and want to adjust volume, the only way you could make the volume adjustment bar to appear is to tilt Fire at an angle (sometimes but not always clicking the small arrow at the bottom displays settings. Why only sometimes is something I need to figure out). When Kindle senses that it needs to go to a portrait mode from a landscape mode or vice versa, it displays the settings at that time. Once you adjust the volume, the video restarts from the beginning and not from where you left it before adjusting the volume. I am sure, there must be some work around, but I haven't found that yet.
Another important feature I think is important to mention is uploading of documents to Kindle. It could be done in two ways. One by connecting your other device like PC that hosts the document or by sending the document as an attachment to an email to your kindle email address. This email address is automatically assigned to you when you register the Fire. Getting the doc uploaded through this email address is something you need to get used it.

As for the kindle applications. Although Kindle's ecosystem is still limited, it is expected to grow very fast. So far I could all applications that I wanted like Facebook, Pandora, Radio streaming, Weather and most importantly Angry Birds - yeh!

So friends, the Fire is an excellent and exciting product that competes with the best product in the world providing the best value in its class of products. Truly an excellent holiday gift, the Fire will bring you blessings from the recipient.


Monday, December 12, 2011

Mobile manifesto - fundamental right to own a mobile device

The person who never handled a tablet before, the person who didn't own a smart phone, the novice and the illiterate in the mobile world. Some one who didn't use skype with iphone, who didn't know how to take photos on a phone and upload them to facebook. The person who didn't know what made one smart phone better than other or didn't have a clue as to why every one thought it was important to interact with their phones but not to the person sitting next to them in a meeting or on a train or in a conference. I was that person until a few days back. None of those things made me totally irrelevant or useless to my kids or to my friends as yet, but it did deprive me of contributing to their supposedly valuable conversations like which smart phone is the best in the market or why it's important for everyone to buy it as if their life depended on it. Kidding aside, barring a few disagreeing heads, mobile technology has become an enabling technology for all of us. While we take such features as finding directions, listening to music, sharing your pictures as granted for smart phones, we are not far removed from the day when all appliances in and outside of your home will be controlled from your phone. In fact, remote 'car starter' application is already available on iphone today. So guys, albeit relunctantly I convinced myself to buy Amazon Fire and a HTC smart phone. What I think about those toys, will be posted soon but the reason for today's post is -

  
While we acknowledge the ubiquitous presence of mobile devices in our lives, how do corporations for which most of us work are reacting to this revolution? Just a decade ago buying everything online seemed liked an idea in distant future, but look at what Amazon and other companies have made possible in this short period of time. Do corporations take mobile computing as seriously as the web revolution? Do they think that making their business mobile friendly is a core requirement to keep them relevant in the market? Why would a company enable its enterprise applications for mobile computing? Are the legitimate security concerns keeping them from going full steam ahead with mobility?

  
These questions are addressed and basically a case for linking mobile computing to the survival of a company is made in a wonderful document which could be downloaded here.  It's a great read for every one remotely interested in mobile computing. It's called mobile manifesto. Eric Lai, who regularly blogs for Sybase is the editor of this wonderful document and it's kind of funny how he puts forward the mobile computing as the fundamental right of a person/corporation.

The document helps an organization in assessing its mobile friedliness, the strategies for mobility adoption and tactics for transforming the way the business is conducted. It includes a score card that computes an organization's mobility status based on how you scored on the questions, the mile stones of our journey to mobile platform from analog devices. The document helps you make some tangible decisions as whether to buy mobile apps or to build them . The key take aways for an enterprise are

  • Open the doors to mobile computing to keep your enterprise relevant to the market
  • Adoption of mobile devices by masses is an irreversible trend. There are more mobile phones than tooth brushes in the world today.
  • Mobility projects are typically shorter than other technology projects allowing you to go full steam ahead. There are low hanging fruits that can provide ROI immediately.
  • Many companies who absolutely opposed to allowing emplyoee's personal device to connect to corporate network previously, are now rolling out plans of adoption in phases.
  • Security of such devices and hence the data protection and application maintenance on them is vitally important and companies like Sybase offer these services at very competitive rates.
  • Last but not the least, and easier said than done, is to eliminate the fear of failure.

For the companies that do not want to host and support their own infrastructure, the cloud option is most attractive one. In fact, number one reason companies go to cloud is the need to open information access to multiple computing devices. Check this article on mashable.com by Matt Silverman for some interesting statistics on cloud adoption. Most intriguing for me is last in the chart about US Gov. employees. It wasnt' the number(48% of US goverment agencies moved to cloud) but the mention of 'cloud-first' policy of goverment agencies that caught my attention. Assuming it's true, adopting a cloud strategy is a no brainer. Because goverment agencies typically are the last to adopt new paradigm in technology, their adoption of  'cloud-first' strategy is proof enough that cloud is gone main-stream. In many companies it's not question of it but when to go to cloud or what applications to migrate to cloud.

Tuesday, November 22, 2011

R programming - It's super!

Last week Sybase announced that its market data analytics platform Sybase RAP - The Trading Edition now supports R. R programming language enables faster algorithm development and handling of huge amount of data to provide its analysis to traders, risk managers and quantitative analysts. For more information on the offering please click here. Sybase also sponsored a webinar on this subject with reasearchers from Yale University. To listen to the webcast click here.

R's official website (http://www.r-project.org/) provides much of the required information if you want to try, but to save you some time, I would like to summerize what I learnt so far from my quick review of R.

Handling and analyzing huge data sets, performing statistical calculations and rendering the results in very professional charts are its strengths. It's performance absolutely blew me away. I tried some of its features on a csv file with 100,000 records with multiple fields and the results were generated instantaneously. Remarkable! According to one of my friends R is used heavily not only in financial companies but also in pharmaceutical or research companies where gene sequencing/analyzing takes place. Bank of America uses R in quantitative analysis. Having spent 10 years with the bank previously, I was surprised I didn't know about it then.The point is, you may not have heard about R because it is not a mainstream programming language, but it is widely used in some industries. Just to show how powerful this language is,
these 2 lines of code is enough to read a csv file with 100000 lines with "|" as separation character and render a scatter plot with values from 2 columns (Durtn & Curs) of this file.


myFile <- read.csv ("Your FileName", head=TRUE, sep="|")
plot(myFile$Durtn, myFile$Curs, main="Duration vs records", xlab="Duration", ylab="Records retrieved")

Google R for more information, but here is the gist.
Some of the features worth mentioning are -

  • R is a language and a development environment built for statistical computing and graphics.
  • It's an open source project.
  • It was built to address some of the short comings of its predecessor called "S".
  • Classes, objects, methods - concepts similar to any object oriented language.
  • The extent of its functionality does not come close to Java or C++ but it addresses a niche and provides functionality that's way easier to use than Java or c++.
  • Lists are ordered collection of objects with no need for the objects to be of the same type.
  • Data frames is a fantastic concept. R can handle multi-dimensional data sets through the use of data frames.
  • Reading a file is as easy as assinging a value to a variable.
  • Accessing a column in a file and running statistical functions on it is accomplished in just one step
  • Charting, graphing is done in one step.
  • Multivariate analysis, a set of techniques dedicated to the analysis of data sets with more than one variable, could be done through R effectively. A use case of multivariate testing is projecting the most effective user behavior (one that yields on multiple clicks) on a website, by moving the assets around on a web page.
  • The output could be sent to the console or a file system resource.
  • Packages are available to plug R into some popular IDEs.
In case you are wondering what other software/languages are used for statistical analysis, please read this thread.
http://www.reddit.com/r/programming/comments/7fg6i/why_are_sasstata_the_default_statistical_tools/

Downloading and trying R is really easy. Just give it a try and let me know what you think.


Saturday, November 12, 2011

Why clique problem is important? Think social networks like Facebook.

A question was asked in last Sunday's Boston Globe, in its 'Etiquette at work' section, on how to avoid appearance of cliques. The person who asked the question has formed a group (clique) at her office that eats lunch together on a day every week. Her manager joins the group once in a while without invitation and the questioner was asking how to resolve this situation. Clearly, cliques are important part of our social life but what does that have to do with computer science?

There was a very interesting article in the MSDN's October issue by Dr. McCafferey. It's the first of a series of articles explaining what a clique is and the importance of finding the maximum clique in a graph and the algorithm to solve the problem. So what is a clique in a graph? Clique is a subset of a graph where every node is connected with every other node. Following paragraph is directly quoted from the
MSDN issue -

Maximum clique problem is encountered in wide range of applications, including social network communication analysis, computer network analysis, computer vision and many others. For graphs of even moderate size, it turns out that the maximum clique problem is one of the most challenging and intersting problems in computer science. The techniques used to solve the maximum clique problem - which include tabu search, greedy search, plateau search, real-time parameter adaptation and dynamic solution history - can be used in many other problem scenarios. In short, code that solves the maximum clique problem can be directly userful to you, and the advanced techniques employed in the alogrithm can be helpful for solving other difficult programming problems. In the following figure the set of nodes 0, 1, 3, 4 form the maximum clique.


Figure 1.
You can find more information on clique problem on Wikipaedia and many other web sites.
While working towards solving this problem, I found myself reflecting on my school years.
When I grew up in India, I disliked mathematics like every one else. Most students were true haters but  others loved it only because it provided an opportunity to score perfectly in a test. Achieving the perfect score wasn't that harder in spite of our dislike, because every teacher and parent instilled  this notion that good score in Mathematics is the yardstick of your intelligence. No matter you liked math or not, or scored perfectly or not, I seriously doubt if any student was interested in knowing why mathematics was needed at all. Any one who dared to raise the question, would typically have to face the silent treatment from the teacher.

Through out the school years, from the  day I learnt to count numbers to the days of triple integration in engineering school, I must have asked this question to myself countless number of times. However, by the time I graduated,  I was grudgingly admitting to myself that mathematics used to be and would forever be the foundation stone of so many fields of science. Despite my dislike, I thought I won't escape mathematics due to my engineering profession, but then, personal computers took over the world, India's software industry boomed and all every student could think of, was becoming a software professional. It provided a level playing field for all, engineers and non-engineers alike. Any one with decent analytical skills could become a programmer. Engineers too became software programmers en mass and marvelled at the thought of not having to do anything with mathematics any more.

Really? Would software profession be that exciting or sexy if you take away search alogrithms by Google, content distribution algorithms by Akamai or algorithms developed for ultra fast equity trading by nerds on wall street? Take away this exciting stuff and what remains is the mundane 'go to', 'if then' statements or uber present 'do while' or 'for when' statements. So, whether we like it or not, mathematics is the foundation required to solve challenging problems .

The reason for the post though, is to post Java code I wrote to solve maximum clique problem for the following graph. Readers are encouraged to see if the code works on other graphs. I didn't want to look at the solution presented by Dr. McCafferey (which by the way is going to be C# code) before I tried it myself.  Creating the data set (storing graph information like nodes and segments) is a challenge in itself. I have taken a short cut by creating a static array of strings to represent segments of the graph. My co-worker Tyler Perry worked on a c++ solution to this problem while I worked on the Java solution. If you like to see more of such posts click on +1 button at the bottom or use share widget on the right to share it with others. My code was tested on the following graph.


Let's do it a little differently this time. Instead of posting the code here, I would like to send it to you directly if requested. Please contact/comment to receive the code.

Saturday, November 5, 2011

More on the Constrained Tree Schema

Last week I posted the Java code that identifies TPCC schema as a constrained tree schema. I also explained why the constrained tree schema is important for an OLTP application. However a little more on the subject is in order, so as to make it easier to understand. First, for those who may not have looked at TPCC schema before, following is an ER diagram showing table relationships.


Figure 1. TPCC schema

Warehouse table has w_id as a primary key while District table has a composite primary key that includes d_id and d_w_id. The d_w_id column is a foreign key pointing to the warehouse table. This pattern continues on to other tables like Customer where its composite primary key consists of c_id, c_d_id and c_w_id where c_d_id and c_w_id are foreign keys pointing to District and Warehouse table respectively.

For those who have used any kind of object relational mapping architecture, this type of relationship may look totally out of ordinary. Typically, ORM tools including Hibernate are proponents of using natural key (like a sequence number) as a primary key of a table. Having a composite primary key on a table, makes it difficult to code Hibernate and java, as you have to create a separate java class for the primary key. But for the extreme OLTP applications, the use of composite primary keys make perfect sense.

In applications that process millions of transactions per minute, it's quite likely that its performance, throughput and latency will diminish as tables in which transaction data is kept start filling up rapidly. Throwing additional hardware at this problem works to an extent. Application redesign through various mechanisms including horizontal and vertical partitioning of the relational tables is used when adding hardware doesn't provide proportionate performance boost.

Many applications use range paritioning to improve performance. It works somewhat like this. Let's say a company, ABC Inc, sells customer durable goods in Unites States and has 4 operational units, one each for its 4 regions - east,west, north and south.

It's customer order entry application generates order number based on the region the order originated from. ABC Inc has built an application logic where an order generated from east region will get a number between 1 and 1 million, the one in west gets a number between 1 and 2 million. To provide better performance, ABC's IT department has partitioned the order table into 4 partitions and has hosted each partition on different servers. For many companies this works without much of a problem. However this scheme has some inherent weakness in dealing with situations such as equal load distribution. If, in case of ABC Inc, one of its region is doing twice as good as others (in terms of of its sales numbers), the server meant to capture the order of that region will be twice as active as others providing inconsistent user response time from different regions. Another factor to consider while creating table paritioning is co-location of related data. If orders table is paritioned, what do we do about other tables? Do your queries often join partioned & non-partitioned tables? Why is this important?

Continuing with ABC Inc. example - the IT department decides to partition payments table too. While it's very likely that a sales person from ABC, would like to see orders from his customers, he or she would also like to see if his or her's customers paid for the items sold to them or not. Just so the application continues to provide best scalability and latency, it is imperative that the partitions of payment table are co-located with partitions of order tables on the same server. The co-location of a customer's order and payment information on the same server is the key, but that adds to the complexity of your application. Add another related table and you could imagine how complex the design is going to be.

Hash partioning solves one of the problems, that of uneven load on different servers. What server particular data resides is decided by some hash algorithm which guarantees more even load on the server. The other problem of co-location of data is solved by the constrained tree schema architecture and hash algorithm together. Going back to our TPCC schema, the tables such as district, customer and stock all have warehouse id in their primary key. So, including warehouse id in the hash algorithm of partioning of all the tables automatically guarantees co-location of data. Which means, data related to one warehouse and its related districts, customers and stock are located on the same server. You may really want to read the "The End of Architecture Era" document for further details. It's a great read for any one remotely interested in database design and improving performance.  The low-latency.com is another great resource for those interested in extreme performing financial applications.

I hope this helps clear some of the concepts behind extreme scaling and performing application/database design. Don't forget to make comments if you have any.


Sunday, October 30, 2011

Detect constrained tree schema programmatically.

Tree schema in a relational database is built on hierarchical relationships between relational tables. It is one of the most widely used type of schemas in OLTP applications today.

I present here the java code to identify if some or all the tables in a schema belong to a tree structure and what table sits at the top of this tree (or root depending on how you view it). The code also identifies the primary keys of the tables in tree hierarchy. This effectively identifies TPCC schema (used to measure TPCC benchmark by hardware and software vendors) as a constrained tree schema.

A constrained tree schema, is used in OLTP systems frequently to achieve very low latency and high through put. If you view TPCC schema carefully, then you would realize that "w_id" column in warehouse table is the root key of this tree hierarchy which also means that all the tables that belong to this tree, have composite primary keys that include "w_id", although w_id column is renamed as "d_w_id" in district table and "c_W_id" in customer table and so on.

Table partitioning (breaking data in a relational table into multiple paritions) is one of the ways an extreme transaction processing applications could achieve the desired scalability. The purpose of this post is to present the java code, if you want more information on constrained tree schema and applications on top of it, this article by  Billy Newport's article is a great start.

Identification of a constrained tree schema and the participating tables is important in evaluating if you could achieve single sited transactions that boost performance of an application dramatically.  "The end of an architecture era" is a wonderful document written by Michael Stonebreaker at MIT and others, that presents a change in application paradigm to achieve high scalability.

The Java code presented below should be used as a guidance only. Adding robust exception handling and follwoing best coding practices would help make this code production ready. The code assumes that you are using Sybase ASE database as a datasource that has "sp_fkeys" stored procedure which lists table dependencies through pk-fk constraints. You may have to rewrite the portion of the code  and the database connection information section, for the database you are using. Identifying TPCC schema as a Constrained Tree Schema is easy enough to do manually because it has only 8 tables, but compare it to an application like SAP R3 which has more than 75,000 tables in its schema and you will quickly appreciate the necessity of automation in this regard.

The code uses the depth first strategy rather than breadth first strategy, not for any specific reason. The java code treats relationships between the tables as a graph. It considers the tables as nodes and the relationship between them asedges of the graph. For more on this subject you may want to read this very interesting chapter from a book, the artful software.

Running this code displays the name of the root table and name of the root key and the depth of the tree. It also displays the list of tables that belong to the tree and their primary keys. There are other smaller trees in TPCC schema, but the one with deepest hierarchy is what we are interested in and the table at the top of the hierarchy is our root table. This information in addition to some other aspects such as tables sizes and volume of different transactions would help an application owner decide on the table partioning strategy.

//The java code
package YOUR PACKAGE NAME


import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;


import com.sybase.jdbc4.jdbc.SybCallableStatement;
import com.sybase.jdbcx.SybDriver;
public class LatestTpcc {
 /**
  * @param args
  * @throws SQLException
  */
 public static void main(String[] args) throws SQLException {
  // TODO Auto-generated method stub
  Connection con = null;
  try {
   SybDriver sybDriver = (SybDriver) Class.forName(
     "com.sybase.jdbc4.jdbc.SybDriver").newInstance();
   sybDriver.setVersion(com.sybase.jdbcx.SybDriver.VERSION_7);
   DriverManager.registerDriver(sybDriver);
   con = DriverManager.getConnection(
     YOUR CONNECTION INFORMATION  } catch (Exception e) {
   e.printStackTrace();
  }
  ArrayList<String> allTables = new LatestTpcc().getAllTables(con);
  // loop through all tables bring their pk, fks
  Iterator tableIterator = allTables.iterator();
  String tableName = null;
  ResultSet rs2 = null;
  HashSet<String> allKeysSet = new HashSet<String>();
  ArrayList<String> pair;
  HashSet<ArrayList> allPairsSet = new HashSet<ArrayList>();
  while (tableIterator.hasNext()) {
   tableName = (String) tableIterator.next();
   rs2 = new LatestTpcc().getFKeys(con, tableName);
   // loop through the result set
   while (rs2.next()) {
    pair = new ArrayList<String>();
    pair.add(rs2.getString("pkcolumn_name") + ":"
      + rs2.getString("pktable_name"));
    pair.add(rs2.getString("fkcolumn_name") + ":"
      + rs2.getString("fktable_name"));
    pair.add("notReached");
    allPairsSet.add(pair);
    // System.out.println("adding pair :"+rs2.getString("pkcolumn_name")+":"+rs2.getString("fkcolumn_name"));
    allKeysSet.add(rs2.getString("pkcolumn_name") + ":"
      + rs2.getString("pktable_name"));
    allKeysSet.add(rs2.getString("fkcolumn_name") + ":"
      + rs2.getString("fktable_name"));
   }
  }
  // now that we have all sets and everything let's light up
  Iterator allKeysSetIter = allKeysSet.iterator();
  String theCurrentKey = null;
  HashSet<String> c = new HashSet<String>();
  HashMap<String, HashSet> keysToColumns = new HashMap<String, HashSet>();
  int maxDepth = 0;
  int theDepth = 0;
  String maxDepthKey = "";
  while (allKeysSetIter.hasNext()) {
   theCurrentKey = (String) allKeysSetIter.next();
   theDepth = 0;
   // all pairs need to be set to non reached for every loop
   // so loop through and set to non reached
   Iterator allPairsSetIterTemp = allPairsSet.iterator();
   while (allPairsSetIterTemp.hasNext()) {
    ArrayList<String> keyPair = (ArrayList) allPairsSetIterTemp
      .next();
    if (keyPair.size() > 2) {
     keyPair.set(2, "notReached");
    }
   }
   // which pair has this key? I will add corresponding key in the pair
   // to this set c
   // so loop through allPairsSet
   boolean reached = false;
   do {
    Iterator allPairsSetIter = allPairsSet.iterator();
    reached = false;
    while (allPairsSetIter.hasNext()) {
     ArrayList<String> keyPair = (ArrayList) allPairsSetIter
       .next();
     boolean keyVisited = false;
     if (keyPair.size() > 2 && keyPair.get(2).equals("reached")) {
      keyVisited = true;
     }
     if ((((String) (keyPair.get(0))).equals(theCurrentKey) || new LatestTpcc()
       .gotAMatch(((String) (keyPair.get(0))), c))
       && keyVisited != true) {
      c.add(theCurrentKey);
      c.add((String) (keyPair.get(1)));
      keyPair.set(2, "reached");
      theDepth++;
      // c.add(String.valueOf(theDepth));
      // System.out.println("the depth of the tree :"+theDepth+" for: "+theCurrentKey);
      reached = true;
      // System.out.println("relationship :"+theCurrentKey+" : "+(String)(keyPair.get(1)));
     }
    }
   } while (reached == true);
   keysToColumns.put(theCurrentKey, c);
   if (theDepth > maxDepth) {
    maxDepth = theDepth;
    maxDepthKey = theCurrentKey;
   }
   c = new HashSet<String>();
  }
  new LatestTpcc().displayValues(keysToColumns, maxDepthKey);
  System.out.println("max depth key : " + maxDepthKey + " depth :"
    + maxDepth);


 }
 private void displayValues(HashMap map, String maxDepthKey) {
  Iterator mapIter = map.keySet().iterator();
  while (mapIter.hasNext()) {
   String key = (String) mapIter.next();
   HashSet<String> hs = (HashSet<String>) map.get(key);
   // display for only max depth key
   if (key.equals(maxDepthKey)) {
    System.out.println("Tree : " + key);
    Iterator hsIter = hs.iterator();
    while (hsIter.hasNext()) {
     String nextKey = (String) hsIter.next();
     System.out.println("element : " + nextKey);
    }
   }
  }
  // System.out.println("the end");
 }
 private boolean gotAMatch(String fetchedKey, HashSet<String> c) {
  // loop through c and see if you find a match
  Iterator cIter = c.iterator();
  while (cIter.hasNext()) {
   String fromC = (String) cIter.next();
   if (fromC.equals(fetchedKey)) {
    return true;
   }
  }
  return false;
 }
/**
 *
 * @param con
 * @return
 * Returns the list of all user tables in this database
 */
 private ArrayList<String> getAllTables(Connection con) {
  ArrayList<String> al = new ArrayList<String>();
  try {
   Statement stmt = con.createStatement();
   ResultSet rs = stmt
     .executeQuery("select name from sysobjects where type = 'U'");
   while (rs.next()) {
    al.add(rs.getString("name"));
   }
  } catch (Exception e) {
   e.printStackTrace();
  }
  return al;
 }
/**
 *
 * @param con
 * @param tableName
 * @return
 * returns information such as primary key & table name of the table that's dependent on this table
 */
 private ResultSet getFKeys(Connection con, String tableName) {
  ResultSet rs = null;
  try {
   Statement stmt = con.createStatement();
   String procName = "sp_fkeys";
   String execRPC = "{call " + procName + " (?)}";
   SybCallableStatement scs = (SybCallableStatement) con
     .prepareCall(execRPC);
   scs.setString(1, tableName);
   scs.setParameterName(1, "@pktable_name");
   rs = scs.executeQuery();
  } catch (Exception e) {
   e.printStackTrace();
  }
  return rs;
 }
}