This section is under re-development. It is not finished, and not fully functional. The current
development stage is pre-alpha and as such, not every feature is even implemented yet.
From Wordpress (Diagmato's Blog)The Newbie Code ObsessionSome of you are going to know what this is about just by the title, and can probably relate to it. Others will hopefully learn something from it, and relax a bit. The situation is best described with an example conversation that happened recently: Him: “Can you please find what’s wrong with this code?” Me: “Hmm, sure, send it to me” Him: “Ok, but before I do, you have to PROMISE, on your life, you won’t use it or share it to anyone else. It took me ages.” Me: “…I won’t. ” Him: “Good Me: “…Except for the, you know, fact that it handles image uploads, maps, and whatnot already? Don’t accuse me of ripping off your code when i’m just trying to help.” He saw sense in the end, but it’s not the first time that’s happened. The problem is that some people get so attached to their ‘hard’ effort, that they think the world is about to try and take it from them, even for the most basic of tasks. So, if this sounds like you (the one paranoid that your code is so amazing, that it will somehow appear across the world whenever someone needs that functionality), then you really need to think about this:
Part of being a programmer is that you find solutions to a problem. That should also be a fun task – one that gives you some sort of satisfaction for completing it, much like a difficult puzzle. You certainly don’t get that when dragging in someone elses code, and you certainly won’t learn much from it either. On top of that, if there are any bugs with the code, the basic programmer will struggle to find it, the expert programmer will probably end up re-writing most of it anyway. Suddenly then, it’s no longer your code. In this circumstance however, an expert programmer just won’t take your code because he/she can easily do it anyway. In fact, if someone was going to “steal ideas from your site”, then they would code their own version, rather than hope they can get your exact code, and implement it. In the example above, the function was one which looped over the contents of a directory, and unlink()’ed all the files, THEN deleted the directory itself. The bug was that, if there was a subdirectory, it wouldn’t delete the directory itself because it still had contents, and unlink() doesn’t remove directories (rmdir() doesn’t delete a directory that has anything still inside it either). The fact of the matter is, it wasn’t exactly some incredible solution to the world’s problems, and any site that handles files in pretty much any way is likely to do something similar. The other point I want to make is, don’t take someone elses code and try to pass it on as yours. Especially if you are then going to ask someone to help you fix it. The moment they ask you a question about it, and you can’t answer it, it becomes obvious. Someone sent me a function that actually had me going for a bit – it was a cleverly done MySQL query using all sorts of degtorad() and distance calculations, for a google map, to work out how far one destination was from another. As soon as I tested him by asking why he was converting to radians, he got completely stuck and thankfully owned up. Finally, if this has sucked the life out of your hard work, then don’t let it. The idea is, you learn from your mistakes. There is no shame in asking for help, just, in trying to pretend you did it all. You are going to run into situations where, something you are excited about coding isn’t going to impress anyone – either that have already done it, or something similar, or something far harder. Difficulty is relative – you will get satisfied in this case, but don’t be alarmed when others don’t share that excitement, and certainly don’t keep going on about it “in case no one could hear you”, because they did – just, it wasn’t actually impressive to them. Xbox Live Achievements – on a webpage?Xbox Live brought about a way for gamers to prove their individual successes with any game on the console. Some were easy – part of the storyline of the game, some were rewards for going above and beyond – some were rewards for doing something very secretive. The end-result is that some gamers tried to milk every last drop from their game in order to collect all the achievements for a game, and thus, a higher gamerscore for their profile. In a last minute dash of experiment, I decided to make an Xbox Live Achievements system for a website, starting with a section that hasn’t officially been released yet. The idea is that:
This provides the user with a few basic reward mechanisms. For completing an achievement, the achievement pops up top centre of the screen for a few seconds, showing how many points it earned them, and which achievement it actually is. Their forum profile lists a table of all their current achievements, and totals. How successful this does is yet to be seen. Aside from the obvious gimmickry, it provides the site’s staff the ability to easily reward members with valuable rewards, and the quick ability to show other members exactly why one of their fellow members are about to receive a brand new iPad, or a £20 iTunes voucher, or something to say thank you in a big way. (for the record though, such rewards haven’t been decided – I am not going to promise an iPad for getting 5000 points just to find out someone Slashdot’s this, and suddenly 200 people qualify. Not that someone would do that anyway). There are basic rules to define an achievement – an achievement:
Now, I certainly don’t think this is going to transform the site overnight, if at all. A lot of members seemed to like the idea, but a lot are also Xbox owners. The system is built in such a way that it is not dominating – if someone doesn’t like the achievements, chances are they won’t see much of the system anyway, but it is there for those who do. Implementation was quick and straightforward, so if this does nothing in the long run, then it’s not the world’s biggest shame. …and one step moreIt takes a few months for a season to come and go, and it seems, the pace of my life. I am no longer employed by the previous company – we parted on good terms. I’m not going to sit and rant about it because, for one, i’m not angry. The company just couldn’t seem to keep the business afloat for a variety of mistakes. All I will say is though, if you are going to create a web development firm, then it is a good idea to know what web development actually is – the directors and seniors were not developers. A manager just should understand something about the core of the business or something will go very wrong, as it did. So, why am I not angry? Because as it currently stands, I am my own boss. A freelancer. I have my own clients, and the next few months are well and trully taken care of. Beware though, none of the clients have anything to do with the company – the contract with them absolutely forbids a developer from externally contacting a client or enticing clients away from the company, and one, I’m not going to try and take on a legal team I can’t afford to fight, and two, the clients of that company wanted sites which just don’t interest me. To keep a potential book shorter, here are the good parts of all this:
I have to admit though, I have been very lucky with this. I certainly doubt many freelancers get to start off with a client who is so well off, “anything you ask for is yours – just get me that site!” His site is technologically the most advanced I will have worked on, which makes it fun. He has asked for features which would tax the most powerful dedicated servers. But he is rewarding well for it. It involves two months entirely dedicated to his site, just for the first stage – get it done, get it out, and that doesn’t involve designing the site – just the raw technical details. There’s the prospect of working with him too – maintaining the site, managing it, and keeping it feature-wise up to date. He wants to start something big – very big, and I feel rather honoured to be a huge part of that, not to mention EXTREMELY thankful for pulling me into the way of life I have aimed for, for so long. Sorry for the way this post was written – I am excited, and that’s after two weeks of leaving the company. The aim if this post is to encourage people of what could be, and what the benefits are. If you are reading this as someone maybe going into college, leaving college, starting uni, leaving uni, somewhere in-between, or just wanting to change career, then maybe I can provide a few thoughts.
Finally though, freelancing isn’t the bliss-end-of-all-bad. You will eventually meet a client who just wants more and more, without paying for it, or who won’t pay you until it is done. You cannot just rush into it because there is the chance of cash on its way next week. You need to agree on something, in writing, with the client, BEFORE DOING THE JOB, and make absolutely sure you both know where you stand. It is better to not get the job than to waste weeks of development for a client who won’t pay you because he first wants “this changed”, “that changed”, “oh wait this changed too”, “and this”. P.S – I am new to freelancing. The thoughts above are only my experiences, which could be drastically different to other people’s. Rest assured there are a LOT of freelancers out there who would disagree with me. Again, I have been lucky so far, and thus, wrote about this very positively. One more step along the road I goHow do I start a post after so long of not posting? Do I bore you with the usual “it’s been a while” and promise not to dissapear again? I’ll save all that and just accept the fact it might happen again. It all depends on how I wish to use this blog from now on. I cannot believe how long it has taken to do the last job. It was the company’s most ambitious job they have taken on so far, and all I can hope is that lessons were learnt across the board. Let’s start with the communication barriers. Take one manager who isn’t, and never has been, a developer. Take a PA who does what she is told very well, and a developer. The process starts.
I’m sure by now you can see the problem. Everyone had different degrees of detail. PHP cannot “smoothly animate images across a page” – Javascript can, but this turned out to be a disaster. Take into account that the Javascript was polling the server every two seconds to see what it needed to do next – on top of that it was supposed to handle animations. By now you should be thinking of Flash, which, if we used it from the start (Which would have been the case if the developers were the ones to actually sit down with the client and find out exactly what she wanted), the project would have been done quicker, and better, because it would have been completely in its own league. Instead, we had a bunch of languages designed to put information on a page, trying to run a game of poker, developed by someone who had never played it poker before. The code we first used was awful. It did the job, but felt like it was written by someone who had just started with PHP, which, if so, would have been an achievement in itself because of the amount of code, working flawlessly. The problem was that variables were given stupid, non-self-explanitory names, the same loops kept appearing in different places (rather than a nice function somewhere), and the general file layout, and “common” tasks to each page were very…shameful. After a few weeks trying to use it, we scrapped it, and I wrote it all from scratch, using an absolutely huge “table” class, which stored an array of “seat” classes. Tasks for the game were broken into steps, and intuitive functions were created to handle each problem during the game. Some of these tasks took far more code than expected, especially working out who won, and with what. The biggest problem in the end turned out to be mostly my lack of understanding of the game. The rules I was basing it off didn’t include “side pots”, and different rulesets would miss out parts of other rulesets. The last couple of weeks were just awful – trying to plug in bits of code to get the class to understand the different rules turned out to be one huge, horrible headache. Every day felt like a drag, as I would spend hours trying to fix one thing, without the help of a debugger other than echoing messages throughout the class, or dumping them to the database and checking the last few hundred records to see what was happening, in which order. The reward for getting that task done, other than a vastly improved understanding of web development, is the next task. One worth £22,000, which is over twice as large as the previous most expensive, ambitious project – which was – you hopefully guessed it, the poker site. Regarding my previous post – I was promoted just before breaking up for Christmas, and have granted the company the ability to take on projects that previously would have been close to impossible for them. They have rewarded me rather well in return too. It requires more responsibility, but has paid off any debt I had before, and then some. I won’t pretend everything is fancy dory though – a full time job doesn’t half take up your time. It would be different if I went home and did something completely different, but programming is my major hobby too. Finding the balance between the programming, and doing something to break it up a bit is a challenge in itself – the simple approach is to not let something dominate your life for too long – break it up, do something different, then dive back in. A long-required updateIt’s been quite a couple of months, enough to fill this entire page in just one post. Life has now taken an entirely different pace, and the past year feels very distant. So here you get the short story. It all started a couple of months ago – I lost my car. We still own it, but dad’s car finally gave up the ghost, and as he paid for my car, and it’s insurance, and because he required one to get back and forth work, he took mine. I couldn’t exactly argue, as all I only used it on weekends. Not having a car is quite a leg-breaker at the best of times, but this time it was a stark reminder that I literally have nothing to my name, in fact, if everyone was to call in repayments, I would be considerably in debt (personally, £1,000 in debt is something I consider very bad, let alone the rest). So, the old way of life couldn’t continue. I could no longer afford to spend 6 months developing something only for it to be side-tracked and move on to something else – too many “start something, never finish”. On top of that, there is no getting anywhere without money, and 3 pages of bank statements of outgoings and no incomings was heading for a distaster very quickly. The only income I was getting was about 60p a week from adsense, which, as soon as I even had a tin of fish for lunch, for one day, was outdone. So, I went to monster.co.uk, and put up a CV. One company however, gave a job description which stood out amongst the others – a company which seemed was looking exactly for the skills I most excel in. So I applied, chased up, and was soon granted an interview, which went very well. The next day, the job was mine. The next week, I was to start. So, it started. It’s an upstairs, rather open office layout with each developer getting a large corner desk each. Everyone gets along nicely. Everyone works together nicely. The teams work well, and the day is very productive. A small sales team sits in the corner with an endless list of phone calls which continues to suprise me – they themselves are not developers, but they sound very enthusiastic about what we do. Every call feels like a pat on the back to the developers, who are really the wheels of the vehicle – hard workers, carrying a lot of weight, but who gets the entire vehicle to their goal. The first week was a mix of dissapointment and suprise – I was dissapointed, they were suprised. I thought I was working very slow, but at the end, they revealed that they gave me the site no one else wanted to do because it was so hard. They expected at least a week, but I did it in 3 1/2 days, including learning their system, their libraries, and packages. The suprise rests with them – the people who looked as if they paid a fiver for a £100 ring. On top of that, the job pays very well – far higher than most university leavers get, often in the same decade they left. It was the first job I applied for, and the first to give me an offer. It is reasonably local, and a very powerful company whose founders shared the same visions I did all this time – they have their own private yacht, have spread to many cities worldwide, yet still feel somewhat small – as if each staff member is valued, rather than just a swarm, replacable at the drop of a coin. They are doing an all-expenses-paid three course formal meal for christmas, and have generous holiday allowance. On top of all that, in 6 weeks I could very well be promoted to the “bespoke” team, which is the very nitty-gritty development, and pays even more, and is more what I want to do. One thing is very important to mention – university did not get me here. The skills the company were after were not covered in any module, even in the third year. University very slightly touched on PHP and MySQL but at such a basic level, that it won’t even blip a company’s radar. If you want to get anywhere you need to show a passion for it – you need to learn at your own initiative outside of education. It’s certainly not games development, but it pays the bills, and then some. It does not feel like a drag going to work on a monday morning. Team work is very rewarded, and people share similar interests, generally. It has also not stopped me getting on with my own work at home, especially when dad actually gets a car, which would spare me three hours a day of extra travelling (his job is along the motoway in the opposite direction, past roadworks and another large town’s traffic). Creating an Effective SunDeveloping the sun was suprisingly easy. The most essential thing to bare in mind is that it is a directional light slowly rotating it’s position around the Z-Axis a distance away from the origin at a given speed. With this, you can guess what the parameters from the sun would be:
The sun is expected to be in a certain position depending on the time of day. At 12:00 PM, the sun would be at it’s highest position, in our case, 90 degrees along it’s route. We therefore need to convert seconds into degrees, which is achieved with the following equation: degrees = (seconds * 360) / 86400; Note that there are 86,400 seconds in a day (60 seconds per minute, 60 minutes per hour, thus 60 * 60 = 3600, * 24 hours = 86,400 seconds). Because both ranges of values (0 – 360, 0 – 86,400) start from 0, we can strip out the minimum range values from the equation leaving the simplified version above. If this is new to you, you will see the full equation used to convert ranges later on, when it comes to working out the colour for the sun. Each frame, the sun’s position, and colour are updated. To do this accurately, the system’s clock comes into play, to give us the time elapsed since the last frame. We use this, and our speed of time variable, to work out how much to adjust the sun’s position. Just apply this new value to the rotation method you use in your choice of graphics Framework. Remember to subtract any previous rotation from this value, otherwise the sun will be picking up speed frame after frame rather than travelling at a constant speed. My implementation handled this when calculating time, as such: timeDelta = gameTime.ElapsedGameTime.TotalSeconds * speedOfTime – oldTimeDelta; Where oldTimeDelta is set to timeDelta each frame. A seperate “timeOfDay” variable is used to keep track of the time by incrementing itself with the timeDelta every frame. If this variable becomes greater or equal to 86,400, then 86,400 is deducted from timeOfDay, keeping itself accurately within the range of seconds in a day. Also a seperate “sunRotation” variable holds the exact amount the sun has rotated in total, and is incremented every frame, and kept within 0 – 360 in the same fashion as “timeOfDay”. This sun position is going to be important with the colour interpolation. “TimeOfDay” itself isn’t critical, but was used during debug to ensure the positioning was correct – it is not absolutely required. Colour InterpolationTo keep a convincing simulation of the sun, we need to adjust it’s light depending on the time of day. A fresh, yellow-ish colour was chosen for dawn, a full white colour was chosen for noon, an orange colour was used to represent sunset, and night time (dusk) was given a dark blue, to simulate a moonlight. We still wanted the world to be visible during night, so using an absense of colour meant that the world was obviously too dark. The blue gave an effective representation of night time, and is a method used in a number of games. Interpolation between the colours was actually the hardest part, but nicely, not actually hard. All it boils down to is maths, and the following method:
The colours are set to four points during the day – it is dawn when the sun is at 0 degrees, 0 seconds. It is noon when the sun is at 90 degrees, 21,600 seconds. Sunset is when the sun is at 180 degrees, 43,200 seconds, and dusk is at midnight, 270 degrees, 64,800 seconds. For each stage during the day, we need to know the following values:
For example, let’s say the sun is between dawn and noon. The “from colour” is set to dawn, the “to colour” is set to noon. The minimum value in the degree’s range is 0, the maximum is 90 degrees. With these values, we can create the interpolated colour using the following calculation: newValue = ( ( (oldValue – oldMin) * (newMax – newMin) ) / (oldMax – oldMin) ) + oldValue And in code: sunColour.R = ( ( (sunRotation – degMin) * (toColour.R – fromColour.R) ) / (degMax – degMin) ) + fromColour.R; Where sunRotation is the amount the sun has rotated cumulatively. Just to clarify, if the sun was moving between noon and sunset, then the fromColour would be noon, the toColour would be sunset, the degreeMin would be 90, and the degreeMax would be 180. The rest of the solution is just passing the colour and sun position to a directional light shader. This assumes you know how to handle this yourself, otherwise you really shouldn’t be jumping into this yet. General Notes and ConclusionAs it currently stands, although the effects are nice to look at, it could be improved in a number of ways. Firstly, I have not mentioned the sky, which in reality, this would change considerably. The skydome in my scene is a good mid-day blue sky with a few clouds, which does a poor job of simulating sunset, and certainly does a bad job at night time. A hopeful solution to this would be to give the skydome a solid, non-textured surface, and represent the clouds as objects of some sort. The colour of the skydome would then be updated depending on the position of the sun. The sky at night is also missing the moon, which could be set up in much the same way as the sun, although as the sun is controlling the lighting, the moon could simply have to follow a path at the opposite side from the sun. A vast improvement would be to involve shadows. Seeing large shadows cast from a mountain would be far better to look at than a scene without shadows. Where there is light, there is shadow, which is not currently the case with my scene. The colours are also not very accurate. The yellow for dawn being the most blatant – it’s just too yellow. The colours were supposed to be very typical whilst also being strong enough to demonstrate that the sun’s colour is interpolating correctly. Whilst care has been taken to ensure the artist’s use of the sun would be straight-forward, the biggest usability enhancement would be to allow the sun to be placed manually in the scene. This is a simple enhancement – the sun’s position would be set to the intersection between a bounding sphere set to the same radius and origin as the sun (the radius of the theoretical sphere the sun moves along, NOT the sun itself’s size). This uses basic picking techniques to check where the mouse’s “pick ray” intersects the bounding sphere of the world. When I get around to any of these enhancements, I will be sure to write about it. Change of LocationI have been keeping this blog up to date, generally, which is more than can be said for my old one. That’s despite running it on a very low powered server through a residential connection. It’s about time I did it some justice by putting it on a much faster server along with other sites I run. That way I can also see if people are actually reading this blog! Clicking something on the admin panel and actually going to the next page within the same minute helps. A good sign is that I have been enthusiastic about giving a head’s up on development. Maybe someone can learn something from any techniques – if so, then that’s a nice bonus too. Switching Between ProjectsThe weekend was good – I revisited some graphics programming, and after all this time, I had some catching up to do. Admittedly, I forgot exactly how to translate and rotate in XNA, and had to revise over an old camera class to spark my memory. It’s all good now though. I started the development of a sun – it moves across the sky, and, if you set the time of day, it will re-position the sun based on the time. This is no difficult feat though – there are 84,600 seconds in the day. The sun will be moving in a complete 360 degrees. So we convert the values from 0 – 84,600 to 0 – 360, and feed this function the time of day (in seconds). The sun also does not behave so predictably colour-wise, however. So far, the sun has a static colour, which would be good for mid day, but bad for sunrise and sunset (and night time!). So for this, I have three colours – dawn, noon, and dusk. The time of day is used to interpolate between the colours as the sun moves across the sky. Now comes something slightly more advanced. If the user wishes to position the sun manually, we need to do some collision detection. We use a bounding sphere the same size as the sun’s theoretical sphere (the radius is the distance from the world origin), and then, with basic picking, see where the mouse’s pick ray intersects the sphere. The resulting coordinate is the new location for the sun, or it can be drag/dropped to avoid accidental replacement if the user accidentally clicks the sky. The value of the sun’s roll (the sun orbits the Z-axis – we have so far used +z for North, +x for East) is then converted to seconds in order to calculate the colour. There are loads of concepts being queued up for the graphics work. The blessing of a hard but fun, rewarding task is also a problem elsewhere. Skinning models, calculating light, calculating procedural grass and terrains absolutely eclipses PHP work. Programming is fun when it is challenging, and PHP offers very little challenge – just monotonous coding, usually in slightly different ways. The biggest reward of website development is seeing people use the functionality and get something from your site. The best part of graphics is everywhere – you are creating your own worlds. There are so many concepts to get through that it offers a constant challenge. Maths, in my opinion, is the greatest strength you can possibly bring to graphics programming. It is everywhere. It is complicated. My maths from school was pretty bad, and I can safely say that most the maths I’ve learned has come from graphics work. It is getting more understandable, but I really do wish I got into it back before things were so critical. Theming, actually not that badBefore, I expressed a problem I had with designing a website. Now, I am writing in a better, more confident mood about it. Actually, the hardest part was finding colours that work together. A problem here is that it is relative. But a counter to that is that if someone is looking for particular content, they won’t sit and review the site’s theme. But again, to counter that, first impressions mean a lot. The obvious is for the theme to look clear, so that the individual can find what they want as quickly an efficiently as possible. That would be one good first impression. No one likes visiting a site they haven’t been to before, and spending even minutes trying to find what they are looking for. People who regularly come back to the site also don’t want to sit in front of a theme that makes their eyes bleed. For the background of the site, it was a tough choice – to go against the site’s main use of blue and peach, I tapped into green’s range, ever so slightly. The result was less taxing on the eyes than the same almost-grey-but-purple shade. I’m also suprised at the detail added by simple drop shadows and rounded rectangles. They are small details, but they make a suprising difference. When using the old theme, the site feels old. The new theme makes it feel fresh, and also more robust, as if the quality of the theme reflects the quality of the functionality. These are still beginner approaches, but they do make a difference. This has given me an interest in image editing to some degree. Finding my way around GIMP and Photoshop would make things a lot nicer when it comes to editing textures for the game engine. Can’t Wait for ChristmasAh yes, Christmas is not too far off. But i’m not looking forward to it for the presents and feasts – my partner in crime and I have decided to set aside a few weeks to work full time on the game engine. The result should be a significant part of a small RPG engine. That is, we should have procedurally generated terrain, with realistic grass, omni-directional shadow maps, a nice particle engine, an inventory system, convincing water, vegetation, and detailed texture mapping on the terrain (with the vegetation following suit). Anything less than the majority of those features would cause a bitter resent for the time and how it was used. Post Launch Day29th September. Time’s ticking by, and the site has really taken shape. Is it ready for the public? Can it survive the public scrutiny? Are the bugs ironed out? Time will tell. Either way, the site has been released at long last. To give it a nice, memorable date, I spent the remaining 5 hours of Wednesday making sure everything was going smooth. The site was then uploaded, the database cleared of test data, and the countdown began – the idea being to initialise the site as close to midnight as possible. The site’s “birthday” then being 1st October 2009. As I type, there are now 11 events. After hours of data inputting, I came across a rather serious dilemma. You see, Britain is divided into counties. These counties have changed over the years, some changed during another change. We are now left with some nice large, general counties, some sub-divided counties, some created to help the Post Office, others created to serve lieutenancies. The trouble here is that I don’t know which county sets to use. One idea is to use all of them, but different sources of information provide conflicting data on which county’s serve which purpose. In the end, I didn’t see much harm in adding all county’s. Except when you try to find all towns/cities in, say, Wales, and the data there is different to how you know it. Take Swansea – it should go in the County of Swansea, yet Yahoo’s directory lists it under West Glamorgan. Which is correct depending on how you look at it, but will someone searching via county get the results they expect? In an ideal world, people would be fully aware that Swansea (and County of Swansea) is in West Glamorgan, which is in turn, in Glamorgan – the original county. But will people select those? That’s not even the entire point – the point is, this is adding a complication to what should be a very quick way of getting information. If someone is trying to find Singleton Hospital, then one would assume they would select Swansea as a county. But in this case, the user would not find the event, as the event is listed under West Glamorgan. So here comes the solution – parenting. County of Swansea is added as a child county to Mid Glamorgan, which is in turn a child county of Glamorgan. Selecting either in this relationship shows the necessary events. But this adds a problem that I, as an individual, am not going to know accurately about, say, Scottish county’s. Information online seems rather disagreeable too. A best guess is to take various maps of the county’s from different years, and compare positions. Towns have also added a problem. Penarth, and Sully were added as towns. Yet an event in Sully is addressed as Penarth. This is apparently, correct. So now, there’s the problem that there is a lot of towns that are part of other towns. As far as I am aware, Sully is a rather small area and cannot really be regarded as a town. As people add events, hopefully this will filter out which towns can be discarded. All I have learned from this is that Yahoo’s directory could be a bit sharper. |