/srv/irclogs.ubuntu.com/2010/08/02/#bzr.txt

GungaDinhow can I know when was the last time a line in a file changed?03:30
GungaDinI tried bzr blame.. but not sure how ot access the revision I see03:30
lifelessbzr log -r 12.34.5603:31
GungaDinit says it wasn't in the branch03:32
GungaDinsorry, my mistake03:33
VornicusHow portable is a bzr repository?  If I create one on a flash drive, will it work on multiple computers and multiple platforms?04:18
lifelessit should04:20
lifelesswe try pretty hard to make it portable04:20
VornicusYay!04:20
spmlifeless: huh, that's a cool achievement! the old 32bit machine vs 64bit machine (tho usually for me it was also x86 to sparc) shifting on 'binary' formats is a pita04:21
lifelessspm: because we work directly over the network on FTP, we're basically lowest common denominator across the board.04:22
spmsweet04:22
lifelessonly thing likely to cause any issue is symlinks, which are different on different OS's.04:22
lifelessbut we don't use them internally, its only if the project has one04:22
spmnod04:22
VornicusAnd since my project already needs to deploy on several devices and I'm building it on the flash drive, I don't think that issue will arise.04:24
bialixhello bzr09:13
* fullermd waves at bialix.09:27
bialixheya fullermd !~09:36
jjannHey. Is there a way to easily restore some (deleted) files/directories from an older revision using the gui? I tried bzr qbrowse, which is very nice to browse the older revision but I'm a bit stumped that on right-click on a folder I don't get anything like "restore"10:46
jjannalso, waht's the easiest/best way to query for something like "what is the latest revision that still had path/to/file?"?10:50
jelmerhi jjann10:51
jelmerjjann: I generally just use "bzr log | less" and then search for the filename.10:52
jjannI need log -v for the filenames to be shown but ok, using the pager's search feature is quite alright, thanks10:54
jjannalthough, I also have the use case of "what's the last revision in which $file still contained $some_string?"10:55
jjannmercurial had 'hg grep' for that10:55
jjann(I don't want to play the "hg is better" card, I wouldn't have switched if I thought that, just mentioned it to maybe help others understand what kind of functionality I'm looking for)10:58
LeoNerdI believe you can just   bzr log [filename]  can you not..?11:00
jjannLeoNerd: that doesn't help as it doesn't print the contents of the files, even with -v (also, $file might not exactly been known, sometimes I used hg grep for cases like "there has been some function called 'foo' at some point in some file, I want it back now")11:03
LeoNerdOh... if you want the contents of the file you can  bzr cat   it11:03
LeoNerdbzr cat -r123 path/to/file  >path/to/file11:03
jjannwell, then I'd need to know the revision to cat.. :)11:03
LeoNerdWhich is what  log   would have given you11:03
jjannas I said, I want to find the latest revision that still contained the string 'foo'11:03
LeoNerdAhh11:04
LeoNerdCould you somehow bisect it?11:04
jjannsure, I could script that using cat and for loop11:04
LeoNerdbzr bisect   I mean11:04
jjannbut I was really hoping for something nice, I mean, isn't using a VCS for restoring old content kind of a major use case?11:05
LeoNerdOh indeed...11:05
jjannwhere are all the tools to support that? ;)11:05
LeoNerd*shrug* Where's the tool to find the last revision where a prime number of files all contained a square number of lines in them? I suspect in practice relatively few people have ever had to ask such a question11:06
bialixjjann: grep plugin may help you, IMO11:06
jjannwell, my case is quite a bit more general than that11:06
jjannbialix: if it does something like hg grep, it will be perfect, I'll look it up. thanks11:06
bialixgrep plugin searching in the history11:07
jjannshould have looked for a plugin to begin with, still not used to so much functionality being in plugins coming from other systems11:07
bialixjjann: https://launchpad.net/bzr-grep11:07
jjannyep, it pretty much exactly does what hg grep does. yay. :)11:07
LeoNerdbzr is very plugin-friendly... A lot of plugin devs write lots of interesting tools11:07
jjannLeoNerd: yeah, that I'm used to from mercurial. the difference with bzr is that quite a few things that are builtin with most other systems I've used are plugins too, just takes some getting used to as I have to start looking for plugins earlier instead of spending too much time searching for the functionality I want in bzr core11:12
LeoNerdI tend to view it differently... Don't look at it as useful core + random addons... It's more a case of "here's a universe of all the possible things, but here is a small bootstrapping core of essentials; feel free to expand it"11:13
bialixjjann: bzr core already has many many features11:18
jjannquit being so deffensive, I already like bzr. :)  It is just a difference, no judgement. bzr does have fewer features in its basic distribution than other systems. it's not even hard to argue that this is an advantage as then all these features can be developed and released independently. :)11:23
jjannI was simply noting that I didn't think to look for grep as a plugin as I'm not that used to that difference :)11:23
=== Leonidas_ is now known as Leonidas
javehello12:41
javeI'm using the bzr plugin with hudson to build emacs, and I'm finding it fairly flaky12:45
javehow can I reduce the bzr issues I get?12:46
javeI get "locked" issues12:46
jelmerjave: Hi12:57
jelmerjave: Can you be more specific about the errors?12:57
javehere is one type of error13:01
javebzr: ERROR: Could not acquire lock "LockDir(file:///var/lib/hudson/jobs/emacs-trunk/workspace/.bzr/branch/lock)":13:01
jave 13:01
javethe problem is that it happens randomly.13:01
javecan you suggest some workaround so I at least get consistent builds?13:02
javecan I just remove the "held" file in the lock dir?13:02
javeheres another type of error:13:04
jave,----13:04
jave| Started by user hudson13:04
jave| $ bzr revision-info -d /var/lib/hudson/jobs/emacs-trunk/workspace13:04
jave| [workspace] $ bzr pull --overwrite http://bzr.savannah.gnu.org/r/emacs/trunk13:04
jave| http://bzr.savannah.gnu.org/r/emacs/trunk is permanently redirected to http://bzr.savannah.gnu.org/r/emacs/trunk/13:05
jave| We expected 214886 entries, but created 21488513:05
jave| ERROR: Failed to pull13:05
jave| Getting local revision...13:05
jave| $ bzr revision-info -d /var/lib/hudson/jobs/emacs-trunk/workspace13:05
jave| null13:05
jave| Finished: FAILURE13:05
jave`----13:05
jave 13:05
* Toksyuryel bops jave over the head with pastebin13:05
javeoops13:05
javesorry13:05
javeheres the pastebin link then, for completeness13:08
javehttp://emacs.pastebin.com/cBZv4HwC13:08
gerard_http://vi.pastebin.com/qmuT2CXD ;)13:17
jelmerjave: You have two instances of bzr trying to access the same branch at once13:22
javenot according to ps13:23
jelmerjam might be able to look more closely into the issue when he joins13:23
javejust to cliarify, I'm trying to set up a hudson buildserver for emacs, and I want the bzr polling and building to be very reliable13:24
javeI can of course to occasional cleanup operations, but I rather wouldnt13:25
jelmerjave: Of course13:31
domascan I import another bzr branch as a subdirectory to my "umbrella" bzr branch?15:50
domashmm, bzr merge-into?15:52
=== deryck is now known as deryck[lunch]
rubbsdomas: I'm assuming you're looking for something similar to svn-externals?15:58
rubbsyou should lookinto bzr colocated branches and possibly bzr-scm15:59
rubbsI'll see if I can dig up links for you15:59
domaswas looking something like merge-into15:59
domasthis is one-time action15:59
rubbsoh, I c15:59
rubbsso, just to clarify, you have a branch and you want to import another branch in as a subdirectory within the first branch. Am I understanding this right?16:01
domasyes16:01
rubbsdo you still want them to have separate histories? or just to merge the histories once and continue on as one project?16:01
domasmerge histories once and continue as one project16:02
rubbsk. let me think a little on that. I'm sure it's possible, I just haven't done it yet.16:02
seanhI've heard bzr has a nice diff utility. Is it possible to use it to diff two files? (not in a bzr repository) If so, where can I find the command?16:02
rubbsdomas: I'm not sure if there is an easier way, but you could in the "subdirectory" branch, create a directory under the root with the name you want the subdir to be and then move all the files in that subdir. Then commit, and then try to merge the two projects together. If I'm understanding this correct, you would then merge in the "new" folder into the umbrella branch.16:06
rubbsif any of that doesn't work you can use uncommit16:06
rubbsseanh: I'm not sure, but bzr help diff says something about --using16:06
domasrubbs: I just lost histories, nobody cares about them16:07
rubbsI've got a meeting, but I'll be back in a bit. someone else may be able to help too.16:07
domas:)16:07
rubbsdomas: oh, ok16:07
domasit wasn't that important to preserve them16:07
domasonly me worked on that sub-project yet :)16:07
domasthanks for assistance though!16:07
seanhApparently something to do with python bzrlib/patiencediff.py, but I can't find patiencediff.py on my system. Might have to checkout the bzr source16:08
seanhHmm, no, bzr is installed, I just don't know how to find the damn file16:08
mgzseanh: `python -m bzrlib.patiencediff -h` ?16:09
seanhYes! Thanks mgz16:11
seanhdiff was giving completely f*cking useless output on some simple config files and making life suck, this works!16:11
=== Ursinha is now known as Ursinha-lunch
=== beuno is now known as beuno-lunch
GaryvdMHi all!16:48
GaryvdMjam: Please could you turn on the ec2 machine. I would like to another build.16:49
jamGaryvdM: spinning it up now. probably takes ~10min to start16:51
GaryvdMjam: Ok thanks.16:51
* jelmer waves to GaryvdM, jam16:51
jammorning jelmer16:52
GaryvdMHi jelmer16:53
mgzjelmer: quick question re 58707416:57
mgz+bug16:57
jelmerhi mgz16:57
jelmerbug 587074 ?16:57
ubot5Launchpad bug 587074 in Bazaar Git Plugin "Branching tree with non-ascii filenames fails (affected: 1, heat: 6)" [Medium,Fix committed] https://launchpad.net/bugs/58707416:57
mgzI take it rev 984 only fixes the utf-8 case?16:57
jelmermgz: sure, what about it?16:57
mgzas you don't seem to have touched the other path.16:58
mgzdo you want a child bug spun up on the other issue with nix any-bytes-will-do repos?16:58
jelmermgz: I'm not sure I follow. what do you mean by the other issue?16:58
jelmermgz: supporting non-utf8 characters in paths imported from git?16:58
mgzwell, what happens if .decode("utf8") fails. yeah.16:59
jelmermgz: If you have suggestions as to how we should handle that...16:59
mgzwell, a nice error explaining the problem might be step one :) (but yup, a sensible end solution is not easy to devise)17:00
jelmerwe have no way of knowing the encoding for sure, and since we need to have a deterministic way of handling these situations, I think we should not support non-utf8 paths at all.17:02
jelmerHaving a better error message would be nice, indeed. Bug reports and patches welcome :-)17:02
mgzokay, I'll spin one off.17:05
jamjelmer: .decode('utf-8').decode('latin-1') is a fairly common solution, as long as it is done consistently17:07
jambut yeah, avoiding the temptation to guess for now is probably reasonable17:07
jelmerjam: The problem with falling back to latin-1 is that I need to have a way of reconstructing the original bytestring17:09
jamah17:09
mgz's similar to problems with checking out bzr repos onto filesystems with smaller idea of what a valid name is17:12
mgzneed some kind of rename, but not mess up the repo for everyone else when pushing17:13
=== beuno-lunch is now known as beuno
GaryvdMjam: We need to upgrade paramiko, as version we are currently packaging gives off hashlib DeprecationWarnings. I'm going to try make the scripts pull a set verison using buildout, rather than us the installed version.17:38
jamGaryvdM: is there a properly fixed version of paramiko?17:38
jamThe other option is to hack pycrypto, which we've done in the past17:38
jamI agree using a script version would be nice, I don't think it is easy to do.17:39
GaryvdMjam: Oh - I just assumed there is a fixed version. I'll check...17:39
mgzseems like there's no release since 200917:41
mgzthere's also http://www.lag.net/pipermail/paramiko/2010-May/001303.html17:41
mgzdid andrew do a ppa or something?17:41
GaryvdMI'm busy looking at the ubuntu package to see if it is patched..17:42
=== Ursinha-lunch is now known as Ursinha
GaryvdMI'm confused now. How dose pycrypto relate to paramiko? alternates, or is the one dependent on the other.17:48
GaryvdMah - paramiko depends on pycrypto...17:50
jamGaryvdM: right, paramiko uses pycrypto to do all of the actual encryption-on-the-wire18:46
GaryvdMjam: Right. I was going to ask you where the patch version is, but It's better if I do a clean room patch so that I can submit it.18:48
GaryvdMhttp://www.dlitz.net/software/pycrypto/submission-requirements/18:48
jamGaryvdM: well, I think you actually need to patch paramiko if you want something that you can submit18:49
GaryvdMThey only accept non US submissions18:49
jampycrypto chose to deprecate (verbosely) an old api18:49
jamI believe there is a new one you can use18:49
GaryvdMOh - ok..18:49
jamrobey hasn't seemed very interested in maintaining paramiko, though I'll also note it switched hosting to git+github18:49
jamhttp://github.com/robey/paramiko18:50
jamshows that there is *some* activity, not sure why there isn't a new release which is compatible with the new pycrypto18:50
jamthere was a bit of a chicken-and-egg since we couldn't have paramiko use the new functionality, until pycrypto released it18:51
jamand then the question is, does paramiko then *require* the new pycrypto18:51
LarsigorengHi, is ther somebody online able to help me with a (hopefully) simple question? I want to go back in history to version 1 of my files, but I am lost in the meanings of all the words revert, uncommit etc.19:17
GaryvdMLarsigoreng: Do you want to commit the old version into the history permantly, to just go back to the old version temporaly?19:18
Larsigorenganyone alive in here? :)19:18
LarsigorengHi GaryvdM! I have old versions of configuration files. I changed these files, committed them...19:20
Larsigorengand fpound out that these changes are no good. So I want to revert that19:20
GaryvdMbla - sorry Larsigoreng. I press ctrl q by mistake19:21
Larsigorengand fpound out that these changes are no good. So I want to revert these changes and this committing19:21
Larsigorengno prob. You got my story?19:21
GaryvdMLarsigoreng: yes19:21
GaryvdMLarsigoreng: Is this in the last revision, or a couple of revisions back?19:22
LarsigorengSo, to be honest, I don't understand your question in the necessary details.19:22
Larsigorengthere is revision 2 with the new versions19:22
Larsigorengand revision 1 with the old versions19:22
LarsigorengI want revision 1 to reappear19:22
GaryvdMLarsigoreng: The easy answer is to do a revert + commit19:23
GaryvdMand the safest...19:23
Larsigorengbzr revert -r 2    ; bzr commit   ?19:23
GaryvdMyes19:23
GaryvdMor bzr revert FILE -r 219:24
Larsigorengthere is not yet a change before I do the bzr commit?19:24
Larsigoreng´cos I did the revert already, but np commit19:25
GaryvdMLarsigoreng: There should be19:25
GaryvdMoh .. wait19:25
GaryvdMLarsigoreng: bzr revert -r 119:25
GaryvdM-r 2 = the current version19:25
GaryvdMyou want to go back to -r 119:25
Larsigorengokay! So -r 1 is the target-version?19:26
GaryvdMLarsigoreng: Yes19:26
Larsigoreng(and not the version I want to revert) Oh then I misunderstood the manual! Let me try!19:26
Larsigorengokay, that looks good. So I reverted it back to version one, and when I committed it, it says this is now version 3.19:28
Larsigorengversion = revision19:28
* Larsigoreng has to do a lot of learning...19:29
GaryvdMLarsigoreng: So you build a better understanding: revert + commit will show in the history (which may or may not be a good thing.)19:30
Larsigoreng*puh* thank you very much, GaryvdM19:31
GaryvdMLarsigoreng: uncommit + revert won't show in the history, but will cause problems  for any body else who has a copy, so only use uncommit if you have not pushed.19:31
Larsigoreng"will show in the history" = is logged and anyone interested in that can find out about that?19:31
GaryvdMLarsigoreng: Yes19:31
GaryvdMLarsigoreng: run bzr log to see19:31
LarsigorengGaryvdM: sorry, I accidentally pressed CTRL+W... ;)19:35
jamjelmer: meliae-0.3.0 is released with a tag and a tarball uploaded, can you package it?19:36
jelmer0.3.0 \o/19:36
LarsigorengGaryvdM: Thank you very much, you saved my evening!19:36
GaryvdMLarsigoreng: Glad I could help :-)19:36
jelmerjam: sure19:36
* Larsigoreng waves goodbye.19:36
GaryvdMjam: How are you using meliae to debug gcc if gcc is in c?19:42
jelmerGaryvdM: I think he means the gcc branch :-)19:50
GaryvdMOh - I see...19:50
GaryvdMlol19:50
GaryvdMHmm - having to use git. I'm finding gitk a bit lacking.19:52
GaryvdMMaybe it's just because I expect gui's to behave ..19:54
* GaryvdM must not be prejudice...19:54
GaryvdMjam: We are packaging pycrypto 2.0.1, and 2.1.0 is out, which claims to add python 2.6 support. May I install 2.1.020:07
jamGaryvdM: 2.1.0 adds different deprecation warnings, but you can use it if you want20:08
jam2.0.1 gives the hashlib warnings20:08
jam2.1.0 gives paramiko RandPool warnings20:08
GaryvdMOk - I see :-(20:08
jam*1* of the reasons we stayed with 2.5 for so long20:09
GaryvdMDo you think I will be a lot of work for me to patch paramiko?20:10
jamGaryvdM: I don't *think* it would be a huge amount, but it is enough that nobody has done it yet...20:11
jamI don't know why robey hasn't updated, for example20:11
* GaryvdM dives in...20:12
GaryvdMjam: Please will you shut down the ec2 machine. I'm done for now.20:13
jamk20:13
jamwill do20:13
jamGaryvdM: if you are going to use it, say, tomorrow, I'll leave it for you20:13
jambut if you are done for a while, I'll shut it down20:13
jam(especially given the TZ differences and my desire to be asleep when you wake up :)20:14
GaryvdMjam: I work on bzr in the same tz as you (after work)20:14
GaryvdMjam: Expect last week when I was on leave20:15
jamup to you. It is nice to shut it down, but I know it adds a fair amount of startup overhead for you to get your stuff done20:15
GaryvdMjam: I'll want to do a release when I have a paramiko/pycrypto solution, or when the next release comes out...20:15
GaryvdMs/release/installer build20:16
jamsure. probably later this week. I think it was actually targetted for the end of last week.20:16
jelmerjam: Any chance I could get you to take a brief look at lp:~jelmer/bzr/controldir, to check if what I'm doing is vaguely reasonable ?20:19
GaryvdMjam: deadline is 12 Aug. Maverick feature freeze...20:19
GaryvdMNext week Thursday.20:19
jelmerjam: (in-progress MP here: https://code.edge.launchpad.net/~jelmer/bzr/controldir/+merge/31573)20:25
jamjelmer: I think you accidentally submitted this for review 2x, or maybe you just directly requested my review?20:25
jelmerjam: I submitted it for review earlier as well but removed that MP20:26
jamjelmer: ah, different numbers, but 2 emails about review, anyway looking20:26
jelmerjam: Thanks. I'm basically splitting ControlDirFormat and "Prober" out of BzrDirFormat20:27
jamjelmer: changing bzrdir.sprout is going to break a *lot* of tests20:30
jelmerjam: I haven't changed it, just moved it to the new base class of BzrDir.20:30
jamjelmer: it seems ok, but at this point it is just moving code around, so I'd have to understand your rationale of why you want it moved20:38
jelmerjam: Mainly I'm looking at running some of the tests that currently live in per_bzrdir only against BzrDir instances and not against other ControlDir instances.20:38
jelmerjam: The reason for splitting out Prober is so that we can add more advanced probing mechanisms later. SvnProber would never need to recurse down - if the current directory doesn't have a .svn/ control directory then the parent won't either. Or we can do probing based on the reply of the OPTIONS HTTP request, etc.20:39
jelmerjam: And in general just cleaning up the distinction between the API and Bazaar-specific stuff.20:40
=== gnomefreak76 is now known as gnomefreak
jamGaryvdM: I submitted your bzrw patch20:50
GaryvdMjam: Thanks20:51
jamGaryvdM: however, i get a text conflict in NEWS20:51
GaryvdMjam: I'll fix.20:51
jamjelmer: I like the concept of splitting things up a bit. BzrDir is a rather large interface ATM.20:56
GaryvdMjam: Done.20:57
jamGaryvdM: submitted20:58
GaryvdMThanks.20:59
jelmerjam: So, did you manage to find the issue with lp:gcc?21:12
jamjelmer: it is a few issues, the original post is sort of long21:18
jambut basically, 150+MB is from loading the whole chk index (.cix)21:18
jamand then about as much memory is in the actual chk maps themselves21:19
jamand about 50M in the dirstate21:19
jamsome of that we can shrink easily, some not very easily at all21:19
jamcix stuff...21:21
jamthe gcc-linaro code is large enough to cover 50k chk pages21:21
jambecause .cix is content addressed, the sha hashes spread that evenly over the whole index21:21
jamso while there are >700k entries, we fit 100 per block, so there are only 7k blocks21:21
jamso to read 50k evenly spaced we have to read *all* 7k21:22
jamI don't have great answers here other than a) custom data structure that is more compact, b) new index for .cix (which I've described some possibilities before)21:22
jambut that is a disk-format change21:22
=== mwhudson_ is now known as mwhudson
=== Ursinha is now known as Ursinha-afk
cwrhi23:13
jelmerhi23:13
cwrI'am trying to pull new revisions from a repo hosted on sf.net to my laptop. unfortunately i get this error message23:16
cwrhttp://pastebin.org/44265123:16
cwram i doing something wrong?23:16
jelmerare you trying to pull into a branch that is bound to another (readonly) branch perhaps?23:17
cwrhmm, not so sure about that. it is bound to a different branch, yes. I used a second branch to commit experimental changes and want to pull them on my laptop now23:22
jelmercwr: In that case, you should unbind the branch first23:24
jelmercwr: it wants to keep the remote ("master") branch up to date but is failing when trying to put the revisions you're pulling in there.23:24
cwrok, did what you said and it is working. nice thank you!!23:26
jelmerlifeless: Hi23:27
lifelesshi23:27
GaryvdMjam: Patched paramiko: http://github.com/garyvdm/paramiko23:28
jelmerlifeless: Thanks for having a look at my controldir branch. To answer your question, the only reason I didn't use ControlComponent as baseclass for ControlDir was that not all ControlDirs would have a ControlTransport that is accessible.23:28
GaryvdMI'm off to bed now.23:28
lifelessjelmer: so, the contract that Martin introduced with ControlTransport is that they will have.23:29
lifelessjelmer: or many bzr commands will go boom, AIUI.23:29
jelmerlifeless: what about e.g. git branches that are basically files on disk or an entry in a dictionary?23:29
jelmerpoolie: Hi23:30
jelmerPerhaps control_url and user_url can be mandatory but control_transport and user_transport optional?23:31
lifelessjelmer: they still have a conceptual url (;branch=xxx), and where there is a url there is a transport23:31
lifelessjelmer: but, controldir is the .git dir, not the branch object *anyway* so that seems an unrelated question23:32
jelmerlifeless: A similar argument applies to e.g. remote git servers though23:33
lifelessjelmer: it seems pretty weak; git: transports can in principle exist23:33
jelmerlifeless: In that case we have only one transport that basically only provides a method for getting a Git smart server client.23:33
lifelessjelmer: a command that prints branch.bzrdir.user_transport, should still do something sensible there.23:34
lifelessno?23:34
jelmerlifeless: I don't particularly see why it needs user_transport rather than user_url.23:34
jelmerI can of course provide two dummy stubs for Git that just implement Transport.base, but I'd like to understand why that extra code is necessary.23:36
lifelessjelmer: consistency of interface is the argument I'd use23:36
lifelessor rather23:36
lifelesscontroltransport is meant to be a base for all these things.23:36
lifelessplease either refine it appropriately, or meet the contract - I don't have an opinion as to which is better.23:37
jelmerlifeless: s/ControlTransport/ControlComponent/ ?23:37
lifeless*I* consider transport == url in all regards23:37
jelmerControlComponent has both control_transport/user_transport and control_url/user_url properties23:37
lifelessso if I was doing the work, I would probably drop the _url attributes and use the transport ones only, but thats me.23:37
jelmerpoolie: ping23:38
pooliejelmer: hi! i'm on the phone at the moment23:38
jelmerpoolie: Ah, ok23:40
jelmerlifeless: Anyway, you make a good point. I'll derive ControlDir from ControlComponent.23:41
lifelessjelmer: thanks!23:41
maxbIs anyone else experiencing lp:bzr/2.2 not clearing its progress displays from the terminal properly?23:44

Generated by irclog2html.py 2.7 by Marius Gedminas - find it at mg.pov.lt!