Showing posts with label software. Show all posts
Showing posts with label software. Show all posts

Thursday, September 17, 2015

two groaners for the price of none

What do you think Bappi Lahiri's build tool of choice is? Why Maven of course. "How so?" you ask. Well, haven't you heard this song from Justice Chaudhury? That's where you'll find supporting evidence (मामा मिया, POM POM). (waits for the groans and the rain of rotten tomatoes to subside).

I wonder why I never thought of the carriage return as the possible fruit of a collaboration between John LeCarre (who created Control) and Ian Fleming (who created M). Isn't it a nice origin to have had for Control+M? (recedes into the shadows before bricks replace rotten tomatoes)

Wednesday, April 03, 2013

open irony

In September 2012, the OpenJDK bug database moved to JIRA. Elsewhere, in the JIRA Knowledge Base, we have a page telling us that the OpenJDK is not supported for running JIRA. Those who are familiar with the phrase "eating your own dog food" (or if you are a company like IBM, the more exalted version, "drink your own champagne"), will now probably wonder about this. In other old news, The OpenJDK wiki uses Atlassian Confluence.

Thursday, July 12, 2012

anti-popular

All the three leading contenders for stealing the torch away from CVS as the version control system of choice have names that no marketing department would have ever chosen. Corporations who sell software invest a lot of money either in their in-house think tanks of agencies specialising in branding and only names that are optimistic and positive, names that suggest innovation and aspiration are chosen.

Who would pick the name subversion? The word means an attempt to overthrow authority. It's also a nice pun on its purpose (version, my dear lost reader, version!).

Who would pick the name git? The word has only pejorative meanings.

And then you get mercurial. The word has both marginally complimentary and often uncomplimentary meanings.

Which brings us to ClearCase. Commercial. Bloated. What a deceptive name!

The moral? If it's got a sly name, it's probably better for you.

Sunday, April 15, 2012

the future of coding practices: bad is good

When you run your searches using your favourite search engine, not all the results ranking high are useful, unbiased or even accurate. Sometimes you need bias to understand what most people think is a good idea. Sometimes, even with bias, it's often difficult to figure out that what most people do is a rather bad way to do things. So you find yourself going to portals like TheServerSide and StackOverflow to get answers that are reasonably sane, answers that you can trust (with a good degree of confidence). Of course, you also stay abreast of articles posted in places like DeveloperWorks, Java.net, JavaLobby and Dzone.

A post came up on dzone a few days ago and it portends a dark future for sensible software engineering.

It's laced with idioms (Though .. but still) that are now getting increasingly common in IT writing thanks to the growing number of people from Patil's Estate, who are able to churn out code like a cow with a bad stomach churns out you-know-what, but never really bothered to get competent in the department of technical writing (or perhaps -- the horror -- even writing itself).

And then we have egregious advice that, one hopes, anyone with a modicum of common sense will ignore. Consider a few samples:

Add comments for each ending block so that it is clear which code block is going to end

Clearly, this gentleman does not use an IDE.

Comment out the code which could be reused in future but could not be implemented this time

Surely this writer has either never used version control or uses it without really understanding what purpose it serves.

The comments on the post are encouraging -- here's hoping that this is not a trend and just an aberration.

Wednesday, March 28, 2012

the machine! it spoke to me

and it said
<nativehr>0x80131904</nativehr><nativestack></nativestack>
Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.
Exception Details: System.Runtime.InteropServices.COMException: <nativehr>0x80131904</nativehr><nativestack></nativestack>
Source Error:
An unhandled exception was generated during the execution of the current web request. Information regarding the origin and location of the exception can be identified using the exception stack trace below.
Stack Trace:

[COMException (0x80131904): <nativehr>0x80131904</nativehr><nativestack></nativestack>] Microsoft.SharePoint.Library.SPRequestInternalClass.GetFileAndMetaInfo(String bstrUrl, Byte bPageView, Byte bPageMode, Byte bGetBuildDependencySet, String bstrCurrentFolderUrl, Int32 iRequestVersion, Boolean& pbCanCustomizePages, Boolean& pbCanPersonalizeWebParts, Boolean& pbCanAddDeleteWebParts, Boolean& pbGhostedDocument, Boolean& pbDefaultToPersonal, Boolean& pbIsWebWelcomePage, String& pbstrSiteRoot, Guid& pgSiteId, UInt32& pdwVersion, String& pbstrTimeLastModified, String& pbstrContent, Byte& pVerGhostedSetupPath, UInt32& pdwPartCount, Object& pvarMetaData, Object& pvarMultipleMeetingDoclibRootFolders, String& pbstrRedirectUrl, Boolean& pbObjectIsList, Guid& pgListId, UInt32& pdwItemId, Int64& pllListFlags, Boolean& pbAccessDenied, Guid& pgDocid, Byte& piLevel, UInt64& ppermMask, Object& pvarBuildDependencySet, UInt32& pdwNumBuildDependencies, Object& pvarBuildDependencies, String& pbstrFolderUrl, String& pbstrContentTypeOrder) +0
Microsoft.SharePoint.Library.SPRequest.GetFileAndMetaInfo(String bstrUrl, Byte bPageView, Byte bPageMode, Byte bGetBuildDependencySet, String bstrCurrentFolderUrl, Int32 iRequestVersion, Boolean& pbCanCustomizePages, Boolean& pbCanPersonalizeWebParts, Boolean& pbCanAddDeleteWebParts, Boolean& pbGhostedDocument, Boolean& pbDefaultToPersonal, Boolean& pbIsWebWelcomePage, String& pbstrSiteRoot, Guid& pgSiteId, UInt32& pdwVersion, String& pbstrTimeLastModified, String& pbstrContent, Byte& pVerGhostedSetupPath, UInt32& pdwPartCount, Object& pvarMetaData, Object& pvarMultipleMeetingDoclibRootFolders, String& pbstrRedirectUrl, Boolean& pbObjectIsList, Guid& pgListId, UInt32& pdwItemId, Int64& pllListFlags, Boolean& pbAccessDenied, Guid& pgDocid, Byte& piLevel, UInt64& ppermMask, Object& pvarBuildDependencySet, UInt32& pdwNumBuildDependencies, Object& pvarBuildDependencies, String& pbstrFolderUrl, String& pbstrContentTypeOrder) +589
[SPException]
Microsoft.SharePoint.SPGlobal.HandleComException(COMException comEx) +27698978
Microsoft.SharePoint.Library.SPRequest.GetFileAndMetaInfo(String bstrUrl, Byte bPageView, Byte bPageMode, Byte bGetBuildDependencySet, String bstrCurrentFolderUrl, Int32 iRequestVersion, Boolean& pbCanCustomizePages, Boolean& pbCanPersonalizeWebParts, Boolean& pbCanAddDeleteWebParts, Boolean& pbGhostedDocument, Boolean& pbDefaultToPersonal, Boolean& pbIsWebWelcomePage, String& pbstrSiteRoot, Guid& pgSiteId, UInt32& pdwVersion, String& pbstrTimeLastModified, String& pbstrContent, Byte& pVerGhostedSetupPath, UInt32& pdwPartCount, Object& pvarMetaData, Object& pvarMultipleMeetingDoclibRootFolders, String& pbstrRedirectUrl, Boolean& pbObjectIsList, Guid& pgListId, UInt32& pdwItemId, Int64& pllListFlags, Boolean& pbAccessDenied, Guid& pgDocid, Byte& piLevel, UInt64& ppermMask, Object& pvarBuildDependencySet, UInt32& pdwNumBuildDependencies, Object& pvarBuildDependencies, String& pbstrFolderUrl, String& pbstrContentTypeOrder) +28077583
Microsoft.SharePoint.SPWeb.GetWebPartPageContent(Uri pageUrl, Int32 pageVersion, PageView requestedView, HttpContext context, Boolean forRender, Boolean includeHidden, Boolean mainFileRequest, Boolean fetchDependencyInformation, Boolean& ghostedPage, Byte& verGhostedPage, String& siteRoot, Guid& siteId, Int64& bytes, Guid& docId, UInt32& docVersion, String& timeLastModified, Byte& level, Object& buildDependencySetData, UInt32& dependencyCount, Object& buildDependencies, SPWebPartCollectionInitialState& initialState, Object& oMultipleMeetingDoclibRootFolders, String& redirectUrl, Boolean& ObjectIsList, Guid& listId) +1750
Microsoft.SharePoint.ApplicationRuntime.SPRequestModuleData.FetchWebPartPageInformationForInit(HttpContext context, SPWeb spweb, Boolean mainFileRequest, String path, Boolean impersonate, Boolean& fGhostedPage, Byte& verGhostedPage, Guid& docId, UInt32& docVersion, String& timeLastModified, SPFileLevel& spLevel, String& masterPageUrl, String& customMasterPageUrl, String& webUrl, String& siteUrl, Guid& siteId, Object& buildDependencySetData, SPWebPartCollectionInitialState& initialState, String& siteRoot, String& redirectUrl, Object& oMultipleMeetingDoclibRootFolders, Boolean& objectIsList, Guid& listId, Int64& bytes) +26593489
Microsoft.SharePoint.ApplicationRuntime.SPRequestModuleData.GetFileForRequest(HttpContext context, SPWeb web, Boolean exclusion, String virtualPath) +608
Microsoft.SharePoint.ApplicationRuntime.SPRequestModule.InitContextWeb(HttpContext context, SPWeb web) +132
Microsoft.SharePoint.WebControls.SPControl.SPWebEnsureSPControl(HttpContext context) +564
Microsoft.SharePoint.ApplicationRuntime.SPRequestModule.GetContextWeb(HttpContext context) +27
Microsoft.SharePoint.ApplicationRuntime.SPRequestModule.PostResolveRequestCacheHandler(Object oSender, EventArgs ea) +918
System.Web.SyncEventExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() +80
System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) +171

W
T
F
!!

Saturday, January 14, 2012

MSFT for MARTA

Has MARTA considered approaching MSFT to help it survive instead of descending ever so slowly into oblivion? Evidence (see below) suggests that the big M might already have support for the ill-fated transit system in its popular operating system

Microsoft's MARTA provider

Tuesday, January 10, 2012

Intentionally Bombastic and Misguiding

I love some of the pages in the IBM Support Portal. They are exhibits of sheer audacity. Can you imagine continuing to pay these guys after the way they treated some of the tickets that people log for their products?

Consider this APAR. An APAR is one of the many acronyms that you get for free when you sign up to swim in the IBM pool. It stands for Authorised Program Analysis Report (aka "yeah, whatever") and (reference) is "an IBM-supplied program that allows you to create a diskette or tape file that contain information from your system to help software service representatives to correct programming problems."

But I digress into the marsh of minutiae. This APAR has been classified as a Permanent restriction. Also known as a feature. (You remember that lovely cartoon, don't you?). In plain English, "we are not going to fix this, so suck up and deal with it. And, by the way, don't forget to send us that cheque for quarterly maintenance and premium support"

As if to assuage the wounded soul who had boldly dared to log this ticket, the conclusion reads After analysis of this APAR we have determined we will not fix this issue due to: low priority/severity. A priority decision has been made that this defect cannot be addressed without neglecting issues of higher importance. Thank you for your understanding. In other words, another three-finger salute while gleefully taking payment for another round of sadism.

Wednesday, December 28, 2011

a piece of py: renaming files

I hate spaces in the names of files. It's a quirk of my personality. I prefer CamelCase as an alternative. I used to like a mix of lower case letters, hyphens and underscores, but that was before my world of Unix and Python was dominated by Java.

However, I am a member of the minority and so I often end up with folders and files whose names aspire to be sentences (or at least parts of speech like nouns, prepositions, pronouns and the occasional verb [To be DELETED: ugh!). Today was one of those days. I had a folder full of PDFs, each with a name of the form Last, First.pdf. I would have preferred a name like FirstLast.pdf. The easiest way to do this was with some shell-fu or Python-fu (with due apologies to the GIMP). I tossed a coin and chose Python. I fired up PythonWin and started working on fragments that would eventually unite into a single one-liner to rename them all (sorry JRR). I wasn't smart enough to reduce it all to one line (I needed the imports and I was not a lambda knight).

import fnmatch, os
# basedir is a string representing the full path to the folder
# containing the poor PDFs
for pdf in fnmatch.filter(os.listdir(basedir), '*.pdf'):
  new_name = ".".join(["".join(reversed(pdf.split('.')[0].split(', '))), 
                      pdf.split('.')[1]])
  os.rename(os.path.join(basedir, pdf), os.path.join(basedir, new_name))

Of cours,e I could have skipped using new_name completely:

import fnmatch, os
# basedir is a string representing the full path to the folder
# containing the poor PDFs
for pdf in fnmatch.filter(os.listdir(basedir), '*.pdf'):
  os.rename(os.path.join(basedir, pdf), 
         os.path.join(basedir, 
                 ".".join(["".join(reversed(pdf.split('.')[0].split(', '))), 
                 pdf.split('.')[1]])))

I think this might be the first time I used reversed(), but since it was a relatively recent addition (2.4 to be precise), I didn't feel too bad. Had I written this code before 2.4 had been released, I would have been forced to use reverse(): this also reversed the list, but did it in place and did not return the list; I would be forced to reverse the list first and then send it onto the join conveyor belt.

I also like how just adding brackets as bookends to items separated by commas, I get a list:

["".join(reversed(pdf.split('.')[0].split(', '))), pdf.split('.')[1]]

Mission accomplished. With some learning to boot. Not bad for a few minutes of work.

Tuesday, October 04, 2011

super seven: the fox is on fire

All hail Nicholas Nethercote!

When Mozilla launched Memshrink to finally do something about all the memory that Firefox never seemed to let go until it had to shrug, I discovered Nicholas Nethercote's blog. Since then I have been reading every new post on his blog, enjoying his writing style and also learning a lot about performance tuning and about the innards of Firefox (I didn't know just how much sqlite was used in all those add-ons). I also waited eagerly for Firefox 7 so that I could see the results of all these fixes and last week I didn't hesitate a moment when I saw the pop-up in the system tray telling me that Firefox 7 was available. Mercifully, the only hacks I needed to get two add-ons working were familiar ones that I had effected when Firefox 5.0 was rolled out.

A few minutes later, after Firefox was back up and running, I could see the difference. I spent some time playing with about:memory?verbose, because the output now contained information about the various compartments that were using memory. Firefox was not lurching to chomp away memory. Instead, it seemed more well-behaved and offered strong testimony to everybody who had worked hard to plug all those leaks and free all that heap.

The only cloud in this silver sky was a news article screaming that Chrome was poised to take over from Firefox as the second most popular browser. Perhaps all these fixes have come too late. Perhaps the article didn't analyse the data correctly and fairly. I don't care much right now as long as Firefox 8 promises to shed even more lard.

Sunday, June 12, 2011

the good thing about loud people in the office

is that you can sometimes expect to hear them utter some gems that make your funny bone dance:
the end result is always to publish a result

first time a second field you have overridden

change that [X] also so we can see that how the [X] can change

Unless commitment is made, there are only promises and hopes; but no plans.. (the speaker was not Salman Khan)

Sunday, May 22, 2011

BSP revisited

I stumbled upon an article featuring a review of the code of the Doom engine. I wish someone had written something like this years ago ...

One of the classes during the undergraduate days required that students deliver a short presentation on some piece of technology they had found interesting. I was interested in computer graphics and games. I was especially fascinated by Doom -- a friend had introduced me to the shareware version of the game and I got my first taste of addiction when I found myself sitting up late one night trying to get past a level without being killed by balls of flame spat out by ugly imps lurking behind closed doors. When Id Software released Quake, I also discovered BSP trees. An article in the now-extinct Dr. Dobb's Journal and articles written by Michael Abrash about working at Id Software (the "Ramblings in Realtime" series) had me hooked. Abrash also made studying computer graphics more exciting. He restored the joy of making the simple things work while the textbooks offered Bresenham's algorithm in a dry academic tone. When I found a copy of Michael Abrash's Graphics Programming Black Book (now out of print, but the PDFs are legally available online) in the bookstore, I did not hesitate in snagging a copy and reading the chapters over and over. Since I was not destined to approach the eerie smartness of Jon Carmack, some of things Abrash wrote about did not immediately make sense. Admittedly, I would have had to "get my feet wet" in code before I could appreciate them or even understand more of the items in Jon Carmack's .project and .plan files in the days when finger was used to support a form of micro-blogging. I also remember

My presentation was an experiment in fonts and slide styles, which made a label like avant garde more appropriate than educational. Since it was all meant more for a grade than for sharing knowledge (although a few classmates were interested in the idea and noted that my enthusiasm for it was evident), nothing more came out of it, although I continued to follow Id (even after Abrash quit) for years and also continued reading articles about game programming. I just never became a competent game programmer.

Sunday, May 01, 2011

the joy of comments

(in code, I mean). Truth is funnier than fiction. Comments in source code, to be precise. Consider, for example, the following worthy addition to the list of classic lines for fortune cookies:
// later is to be
or a declaration of principles:
// THIS method is not calling anywhere
or a declaration of the laws of the jungle:
// All transactional processing classes are responsible
// for calling this method and for providing what action is provided
or perhaps an example of stating the obvious:
// This abstract class houses default and common
// implementation for some interfaces

// This class is the message-driven bean implementation class
// which acts as the listener to queues which hold the messages that affect other 
// business objects
it is unclear why the following comment should even appear in code (perhaps the reader is not expected to be a developer):
// It is restricted to change this API without testing all the references
how does one say a lot without making much sense? how about (some of the funny names in camel case are euphemisms to protect the innocently dangerous; any others are changes just to keep the legal eagles awbay):
/*
 * DefectNumberInOneTrackingSystem Fix : Added this flag becoz
 * misspelledMethodName() in Abstract Object Check Against Null String of
 * FieldThatCannotBeDisclosed String While in This Defect Although it was Not
 * ______ Update But then also FieldThatCannotBeDisclosed string was
 * Not Null Becoz the default value stored in database is
 * 0.0 So it is Not Null and MisspelledFieldName Become true and
 * Then it dont set the AnotherFieldThatCannotBeDisclosed if it is True...See
 * DefectNumberInAnotherTrackingSystem
 */
{includes material that previously appeared hereabouts}

Friday, April 22, 2011

how do you puke in Java?

RuntimeException up = new RuntimeException();
throw up;

the demonstrative manager

Allow me to introduce you to a burgeoning breed of managers. The ilk shares several traits with existing strains of Manageris Vulgaris, the genus of airheads sired by the booming industry of Information Technology in the South Asian continent (to be more precise, the land of Bhaarat). Familiar is the curious talent for lapping up the noodles of crapspeak (going forward, synergy, stakeholders) while remaining clueless about the fundamental elements of English grammar (the question mark and the period are used interchangeably and one often has to read the email aloud with various emotional and tonal flourishes to determine which mark of punctuation would be most appropriate; brevity is replaced by a mouthful of randomly abbreviated words that make you wonder if the writer was in a hurry to get to a rendezvous with Mother Nature).

The new mutation of this species has a fresh appreciation for deixis. To understand what such an individual means in a single sentence without any context is more challenging that deciphering a haiku written by Mirza Ghalib. Consider the following example:

You send me those. I will check if that is what all people is doing. If yes, then we will have to do this.

The writer of this wondrous explosion of verbiage would have been so much more comfortable writing monologues for the Oracle in The Matrix than being effective in professional communication.

I leave you with a sample from a less-qualified exponent of this demonstrative delivery. Fear not, dear reader, for this person will also rise in the ranks and demonstrate some fantastic foinery:

Right now we are doing like that only. If we do like this then we need to [...]

Wednesday, April 06, 2011

linky dinky

The Wall Street Journal has an article that tells us things we already know: India churns out a lot of graduates from a system that is rife with insufficient funding, peppered with corruption and laced with stale knowledge; these graduates don't really cut it when it comes to the jobs that have flooded the market and contributed to all the "prosperity" the nation seems to be enjoying (the call centre kind). I liked what Vijay Thadani of NIIT had to say: If you pay peanuts, you get monkeys.

I have been learning and using git for quite some time now and my experience with the DVCS (although not as D as I would have hoped it would be) has been fun so far (endorsements from various open source projects and enthusiastic presentations from the likes of Matthew McCullough have also helped). Windows is still a secondary platform for it and EGit offers IDE integration but is still in incubation. That's why I always wondered about that other DVCS that I had heard people talk about: Mercurial. I had heard about it first from a friend at Sun as they were moving to it. That was probably when the OpenJDK project moved over to Mercurial as well. Mercurial is written in Python and when Python moved over to Mercurial, it seemed to make a strange kind of sense (the dogfood kind). The reasons, however, were more interesting and backed by some thorough investigation. Simply put, the team chose something that would meet the needs of the developers as much as possible. The articles about the journey from PEP 374 to PEP 385 have been very interesting. They will also help me learn how to use Mercurial and MercurialEclipse.

Friday, November 19, 2010

a rose by any other name

might just be a confused flower after all. Rational is such a funny name for that company acquired by IBM (another firm that thrived in large complex pieces of software). ClearCase is anything but. This brings us to Adobe Acrobat, which, like a baby, grew with each passing release. It's a young man now and a really well-fed one at that. 145 MB is a good reason to stop using acrobat as a name (elephants as trapeze artists work well as jokes in cartoons not as disk-churning memory-hogging DRM-crazy software applications) and move to a bland name like Adobe Reader. Ooh. Consider Foxit Reader as an alternative (if you already haven't). At 6.56 MB, it saves you some disk space and also does exactly what you want it to do: open PDFs. The next time you want a chihuahua, take a moment to wonder why you would choose an elephant instead.

Sunday, October 03, 2010

error messages for developers

All its corporate weight and worth could not save Reliance from the blight of the generally terrible state of web pages in Indian portals. The portal for Reliance India Call is an eyesore. It looks like a song sequence from a bad Raj Kanwar film. Make that a song sequence from any Raj Kanwar film. Of course, the portal offers a "Contact us" link right next to the apostrophe abusing FAQ's on the right top corner. Click that to be taken to a form that asks you for a lot of required information and a description of your problem. There is a field called "Issue/Feedback Type" which offers a dropdown control to select values. Once you do that, the whole page blinks (or flashes, if you're downloading a lot of songs and movies while doing this or if you have a modest Internet connection) and the values in the dropdown for the field below called "Sub Type" are generated based on your selection. When you finally get to the "Description" and type out your woes and click "Submit" you are likely to see the popup below:

illegal

The only way you can find out what was "invalid" is to view the source of the page. Search for this error message and you will stumble upon the following line:

ctl00_ContentPlaceHolder1_regvDesc.validationexpression = "^[a-zA-Z0-9.\\s\\#\\-\\.\\,\\/\\(\\)]{1,1000}$";

Of course they expected you to do this and to know regular expressions rather well so that you could figure out that they did not want you to put a semi-colon in that field. How simple!

addendum [October 06, 2010]: Add double quotes to that list. I think Reliance should just support a tweet appender instead of the form and limit all submissions to short inarticulate bursts of alphanumeric vomit.

addendum [October 19, 2010]: add question marks to that list. At this point, I wonder if anyone really cares. Try complaining about network connectivity and be prepared to try and figure out what the correct for the phone number is. All you get as a hint is a popup telling you that the format is incorrect. If you get it right the first time, congratulations.

Thursday, June 10, 2010

recommended additions to every software developer's lexicon

There's a thread over on Stack Overflow dedicated to jargon from the world of programming. Some of these are old nuggets familiar to fans of Dilbert and The Jargon File; others are eye-opening revelatory catalysts for belly-aching laughter. My favourites so far are the taxonomy of bug reports, the amazing moniker (why didn't I ever think of this before?) for the Sun standard for braces and a delicious alternative to spaghetti code. If you've been a student of computer networks, hot potato and hot potatoes seem so appropriate as slang for HTTP and HTTPS. Excuse me while I fetch my guts.

Sunday, April 04, 2010

the appropriation of abbreviations

CPU used to mean Central Processing Unit until Oracle decided to use the abbreviation to refer to Critical Patch Updates; apparently patches did not seem to exciting for some corporate lover of cornflower blue.

Tuesday, January 26, 2010

what if someone pretending to be confucius used Eclipse

depecrated (deserves to be execrated); chinese internationalisation (whatever); this abstract class houses default and common implementation for some interfaces (very observant); all tranctional processing classes are responsible for calling this method and for providing what action is provided (schizoid indecision); used to identify newly instantiated instances (काला टीका?); later is to be (absolutely). This is a heck (what the hack!).

Someone would get paid for writing code that invoked the toString() method on an instance of java.lang.String. Meanwhile, in the woods, someone talks about methods consuming a lot of memory consumption (Borges would be proud, no doubt).

 
Creative Commons License
This work is licensed under a Creative Commons Attribution-NonCommercial-NoDerivs 3.0 Unported License.