<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-11885757</id><updated>2012-01-02T20:50:48.235-05:00</updated><title type='text'>Medicine for the Sky</title><subtitle type='html'>Curtis Autery's ramblings.</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://cautery.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11885757/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://cautery.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><link rel='next' type='application/atom+xml' href='http://www.blogger.com/feeds/11885757/posts/default?start-index=101&amp;max-results=100'/><author><name>Curtis Autery</name><uri>https://profiles.google.com/107677530285177731535</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh4.googleusercontent.com/--J3C_khtLqQ/AAAAAAAAAAI/AAAAAAAADT4/e2Qa5ncfAMk/s512-c/photo.jpg'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>180</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-11885757.post-3799777866509520020</id><published>2011-12-07T17:27:00.000-05:00</published><updated>2011-12-07T18:06:23.588-05:00</updated><title type='text'>Encounters with the police</title><content type='html'>&lt;p&gt;The cops have had an awful lot of bad press lately, for example the interactions with OWS protestors such as &lt;a href="http://www.news.com.au/top-stories/police-criticised-after-tent-dress-torn-off-occupy-protester/story-e6frfkp9-1226215166929"&gt;this one&lt;/a&gt;, and &lt;a href="http://www.heraldtribune.com/article/20111204/ARTICLE/111209980/2416/NEWS?Title=SPECIAL-REPORT-How-Florida-s-rogue-officers-remain-on-the-job&amp;tc=ar"&gt;this sobering article&lt;/a&gt; from the Sarasota Herald-Tribune, or &lt;a href="http://news.yahoo.com/swat-teams-shooting-marine-causes-outrage-184928360.html"&gt;this report&lt;/a&gt; of a former marine killed in his home by a SWAT team who erroneously thought he was a drug dealer. And erroneously thought their team member who tripped had just been shot.&lt;/p&gt;&lt;p&gt;My personal encounters with law enforcement have been qualitatively different. I have interacted with the police 26 times, and although that number seems large, none of them ended in an arrest, and in only two seemed like good material for an episode of Cops. In rough order of occurrence (I'm approximating my age on a few of these), they are:&lt;a name='more'&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;1. When I was about 13, I was on a canoe trip where a boy drowned, and the police were called to investigate. A friend of the victim broke down crying at one point, and the officer taking the report was very consoling to her.&lt;/p&gt;&lt;p&gt;2. At 15, my bicycle was stolen. My stepfather called the police, an officer came to our apartment to take a report. Our apartment smelled of weed most of the time, as my folks smoked quite a bit of it. The officer either didn't notice, or didn't mention it. He took the report, informed us that it's rare to make a recovery, but they would nonetheless give it the ol' college try.&lt;/p&gt;&lt;p&gt;3. At 16, a pair of officers knocked on our open apartment door late one night when we were sleeping, to verify we weren't being robbed. I assume a neighbor saw the door accidentally left open and called on our behalf.&lt;/p&gt;&lt;p&gt;4. Also at about 16, a group of my friends and I were horsing around at Graceland shopping center, jumping up and smacking a high awning. We were mainly just seeing who could jump higher and not trying to vandalize it. A patrol car slowed down and warned us to knock it off.&lt;/p&gt;&lt;p&gt;5. At 17, I took a late-night walk, and ended up near some closed office buildings. A patrol car came to make sure I wasn't vandalizing the buildings or trying to break in. I was in sort of a bleak mood that night, prompting them to give me the "Problems at home?" quiz. I passed, apparently, and they shood me on, asking me only to stay on the sidewalk so as not to look like a prowler.&lt;/p&gt;&lt;p&gt;6. At 18, my car was in the shop, and I wanted a late-night snack, so I walked down to a local gas station. I took a shortcut back home, crossing through a car dealership and onto the train tracks behind it (Jack Maxton Chevrolet by 161 and Proprietors, if you're familiar with Worthington). Unbeknownst to me, a car had recently been stolen there the previous night, so the police were patrolling around it more. An officer jogged up to me on the railroad tracks, but saw that I was just drinking my Mountain Dew and strolling along, so he explained all this to me. When he asked my name, he even went so far as to say I wasn't obligated to answer. I cooperated, said I'd stick to the sidewalks in the future, and he let me go.&lt;/p&gt;&lt;p&gt;7. My first speeding ticket, and the first time I thought I was about to get shot. At 18, I thought I was invincible behind the wheel of a car, and took late-night drives through the hilly backroads between Worthington and Linworth, and just went nuts, flying around curves, pedal pegged. During one of these outings, a cop passed me going the other way. For some reason, I assumed he wouldn't even try to turn around and catch up to me. About a mile and a half later, I was proven wrong when I saw his lights in my rear-view mirror.&lt;/p&gt;&lt;p&gt;I pulled over, and I knew that I would be asked for my registration, which was in the glove box... you can see where this is going, can't you? As the officer approached my window, he saw me rooting around in the glove box, and barked out a sharp "HANDS ON THE WHEEL!". I complied immediately. He followed this by questions about my drinking (none, by the way, I didn't start drinking until my late 30s), how he'd like nothing more than to haul me away to jail, but a moving violation wasn't enough cause, and he left me with a ticket, and went on his way.&lt;/p&gt;&lt;p&gt;A lot of that was basic "scared straight" material, which I saw for what it was, but I reflected a lot on how he seemed ready to draw on me when he saw me in the glove box, and I decided to stop driving like a maniac. In hindsight, this probably saved my life, as I was less likely to wreck in an infrequently traveled backroad in the middle of the night.&lt;/p&gt;&lt;p&gt;8. At 19, my cousin Glenn and I were driving back from a city east of Winston Salem, NC (possibly Raleigh, but I can't remember for sure) where we were helping to open a new "Shoe Show" store. I was a passenger, and Glenn was trying to make good time on the way back. A state trooper pulled him over for speeding, and asked him to step out of the car.&lt;/p&gt;&lt;p&gt;As it turns out, this wasn't a bad thing, the officer just wanted to scold Glenn in private rather than embarrass him in front of me (southern politeness - I was still sort of a kid, making Glenn, about 10 years my senior, an authority figure by default. It would demean that authority to get publicly scolded. This probably sounds confusing to you Yankees, but that type of thing is a standard tenet of our culture). Glenn was completely "yes, sir" and "no, sir" to the officer, who let him off with a warning.&lt;/p&gt;&lt;p&gt;9. At 19, I was a passenger in a car with my friend Steve, who suffers from sleep apnea. We were both beat after coming home from either a party or a concert, and he swerved a little, and was pulled over. After the officer assessed that we were both just tired and not drunk, he told Steve to be careful and try to get off the road soon and find a bed.&lt;/p&gt;&lt;p&gt;10. At 20, I would typically close the pizza store I worked at, and then head off to a 24 hour gym to work out before I went home. On one particular night, I went flying through a speed trap, and was pulled over. The officer asked me where I was going in such a hurry. "World's Gym," I replied.&lt;/p&gt;&lt;p&gt;As luck would have it, that was the gym that a lot of police officers worked out at. I was let go with a warning (possibly from the serendipitousness of my destination), and also informed that one of my brake lights was out.&lt;/p&gt;&lt;p&gt;11. At the tail-end of my reckless youth, I liked to play on playgrounds in the middle of the night with friends. On one particular night, my friend Lisa and I stopped by Colonial Hills Elementary at about midnight after picking up some Jolly Pirate donuts. We sat on top of the school's jungle gym, ate our donuts, and chatted.&lt;/p&gt;&lt;p&gt;After a while, a cruiser pulled up and shone its lights at us. The officer asked what we were doing, and I flashed him the Jolly Pirate bag I was holding. "Just eating some donuts". He then asked our ages (18 for her, 20 for me), and then told us simply to "stay out of trouble". He left, without even demanding we leave the school.&lt;/p&gt;&lt;p&gt;12. At 21, the pizza store I worked for had run out of an ingredient, and I was sent to another store to borrow some. Trying to hurry back, I ended up read-ending someone who stopped suddenly. The police arrived to take a report, where I admitted I was at fault and was cited for unassured clear distance.&lt;/p&gt;&lt;p&gt;13. Also at about 21, I was pulled over for speeding on the West Virginia turnpike. The state trooper invited me back to his patrol car, where I sat in the front as he filled out my ticket. He was very chatty, and was listening to classic rock in his car. I think maybe he was just lonely and wanted someone to talk to. Nice guy, if you don't count that he gave me a $60 ticket.&lt;/p&gt;&lt;p&gt;14. At 22, I was an assistant manager at a pizza store, and had a free night with nothing to do at 12:30am. I decided to play a prank on that night's closing manager, so I parked in the empty lot next to our store and snuck over, then quickly unlocked the back door and jumped in to the suprise of the people doing closing cleaning and paperwork.&lt;/p&gt;&lt;p&gt;Meanwhile, a patrol noticed my car in an empty parking lot, and ran my plates. By some means, they put two and two together and phoned the store and asked if I was inside... then asked me to come outside. I did, and the two officers spread out around me until they formed an equilateral triangle with me as the third point. They were clearly on edge and expecting trouble.&lt;/p&gt;&lt;p&gt;It turned out that I had never paid my ticket from number 13. Combine that with my car being in a vacant parking lot, and they thought maybe I was a criminal robbing the store. A few minutes of conversation assured them otherwise, and they ended up not citing me with anything, but taking my car's license plates (!) until the matter with the West Virginia tickets was resolved.&lt;/p&gt;&lt;p&gt;I'm not sure if taking my plates was legal or warranted, and it did come close to causing me to lose my job (managers have to be able to drive). I managed to get the ticket cleared up in a few days, and got my plates returned to me, so there wasn't any long-term consequence.&lt;/p&gt;&lt;p&gt;15. Also as 22, shortly before stepping down from pizza management to the less stressful and equal paying job of delivery boy, an officer came into my store to pick up a pizza. This happened a lot, and I haven't listed the score or so times I've handed an officer a pie and told him to have a nice day. In this instance, however, a young man, college age, who was in the store prior to the officer's arrival stiffened and was visibly nervous. His order came up first, so I handed it to him. He said an abrupt "thanks", and quickly left the store.&lt;/p&gt;&lt;p&gt;After he left, I mentally played back the conversation he had with Dave, my inside closing help that night, and didn't remember him ringing the man up. "Did he pay?" I called to Dave. He replied "I thought you rung him up."&lt;/p&gt;&lt;p&gt;Without speaking to us, the officer (Shicks, I think, from the Westerville police) got on his shirt-mounted CB and called for a car to come pull the guy over as he drove away. Being a trained observer, he knew the make, model, and color of the car, the road the car left on and which direction he went.&lt;/p&gt;&lt;p&gt;About 10 seconds later a police car with lights flashing came flying past the store. About 5 minutes after that, the ghost-white, shaking man came back in, stammered out "I'm so sorry," and paid for his food. Our running theory is that he was holding, and thought he was about to get busted. To my knowledge, the officers didn't pursue that angle. Scaring the crap out of a college boy is more fun than a nickle-bag bust, I imagine, plus the amount of paperwork to show probable cause for a search was probably insane back in the pre-9/11 days.&lt;/p&gt;&lt;p&gt;16. At 23, an SUV rear-ended my Ford Escort, smashing my back window and cleaning my clock pretty badly. An officer ticketed the man who hit me, made sure I was ok and didn't need a doctor, and called a tow-truck for me.&lt;/p&gt;&lt;p&gt;17. Also at 23, I was booking back to the pizza store at 1:45am after making my final delivery one Saturday night, and went a little left of center on the final left-turn onto the road the store was on. An officer pulled me over, and sat there until backup arrived. Then both officers exited their cars, one approached my driver's-side window, the other took a vantage point behind my car and to the right.&lt;/p&gt;&lt;p&gt;So basically I'm sitting there with my store driver's hat on, the empty pizza bags beside me, and about a block from the store. Despite that, I was at first unable to convince the officers that I was working and on my way to the store, rather than going home from a bar or a party. "How much have you had to drink tonight?" came up more than once. I blew a 0.0 on their portable breathalyzer, naturally, and they then accepted my story, and let me go without a ticket. Possibly that was their way of apologizing for the wrong assumption.&lt;/p&gt;&lt;p&gt;18. Again at 23, near the time I stopped working pizza, I was asked to give one of the teenage counter-girls a ride home since her ride fell through. I combined this with a delivery going the other direction, so I sped a little so I could finish the delivery, drop her off, and get back to the store quicker, as we were short-staffed on drivers that night. I was pulled over for speeding, however, making my efforts at timesaving moot.&lt;/p&gt;&lt;p&gt;It was near Christmas, and the young lady riding with me, fit and lovely, was wearing a snug sweater that the officer was visibly distracted by. He casually and quickly gave the standard warning speach "Hey, it's close to the holidays, and kids are out going wild, be careful on the streets... man, that's a nice sweater!" I was let off with a warning, which I attribute to Tracy's charm and the young officer's libido.&lt;/p&gt;&lt;p&gt;19. At 24, I had quit pizza, started working at CompuServe, and moved my girlfriend in with me. She would later become my wife, and later still would become my ex-wife. I received a phone call one evening when she and I were watching television. The call was from a police officer who introduced himself politely, asked if I was Gayle's son, and said that she and her friend needed a lift home.&lt;/p&gt;&lt;p&gt;I got the address, and my girlfriend and I went to pick them up. On arrival, the officer gave me the full story. He pulled them over for drunk driving, and was more interested to see that they got home safely than to cite anyone (my mom and her friends were pretty entertaining and likeable back in her party days, which probably helped). I said thanks, my girlfriend and I each drove a car, and no one got ticketed or sent to the drunk tank.&lt;/p&gt;&lt;p&gt;20. At 25, I let my tags expire. Life with a new baby was sort of hectic, which I rationalized as an excuse to keep procrastinating. I stopped by the bank on my morning commute one day, and wanted to finish my business and get to work as soon as possible. The side road the bank was on intersected a major road (Sawmill) and a block down the main road to the left was a stoplight. The light had just changed to green, sending traffic my way, and I had the opportunity to beat the traffic, provided I blew a stop sign. I scanned for pedestrians or other obstructions, and punched it, pulling in just ahead of the first car.&lt;/p&gt;&lt;p&gt;I pulled into the left turn lane (for Snouffer), and got caught at the light. Mozying up behind me came the first car from the previous light, a Columbus police officer. And he sat there and waited. And I spent the next 90 seconds thinking about all the opportunities I had to renew my tags but decided not to. The light changed, his lights came on, and I took my lumps (figuratively). I think the ticket for the stop sign and driving on expired tags was a little north of $100.&lt;/p&gt;&lt;p&gt;21. At 27, my wife and I took a vacation to North Carolina with our toddler daughter, and decided to stop at a hotel in West Virginia when little Stacey started getting fussy. While driving down a service road to the hotel, a police car coming the other way stopped and turned his emergency lights on and approached us on foot, cautiously.&lt;/p&gt;&lt;p&gt;"Where you guys heading?" asked the officer"Yon hotel," I said, pointing. I probably didn't say "yon hotel".&lt;/p&gt;&lt;p&gt;He looked us up and down, shone his flashlight inside our car, and explained that we were driving the same make and model of a car they were looking for. He didn't say what the guy in the car was wanted for, but after assuring himself that no one was hiding in our floorboards, he tipped his hat, bid us good day, and ambled back to his car. He probably didn't bid us good day.&lt;/p&gt;&lt;p&gt;22. At 30, my first wife and I had split up, and I was for a few months dating a woman in Kirkersville when I was living in Dublin. A few times a week, I would trek the 40 miles to her house, play with her kids, go out to dinner, and trek back home. She was great, her kids were great, and despite our good relationship, we just sort of stopped calling each other. My assumption has always been that Tia looked up my court records and saw that my divorce wasn't actually finalized like I told her it was. I felt like an asshole for a long time after we stopped seeing each other, especially for how her 8 year old would feel abandoned by me. Thinking about little Merica being sad was the main reason I didn't date anyone else for the following 7 years.&lt;/p&gt;&lt;p&gt;Anyway, police, right... During that time, I drove this little Saturn whose previous owner drove the hell out of it. Shortly after I bought the car, a host of things started to go wrong with it faster than I could afford to get them repaired. During one of my 40 mile treks back from Kirkersville, my headlights went out. My parking lights still worked, and so did my brights. It was about 11pm on a weekday when I was heading back, so there wasn't much traffic. I alternated between driving with my brights on, and switching to just parking lights when a car came from the opposite direction, so as not to blind them.&lt;/p&gt;&lt;p&gt;I made it almost all the way home, and then about half a mile from the freeway exit back to my neighborhood, a cop pulled me over to see what was going on. I explained the situation, and promised to get the car to a shop come daylight. He advised me just to leave my brights on and not worry about blinding people, and let me go.&lt;/p&gt;&lt;p&gt;23. At 32, I was at the apex of a horrible undiagnosed sinus infection that would probably eventually have killed me had I not found a good ENT to perform a septoplatsy. For about 6 months, I kept going to my family doctor complaining of headaches that put pressure on my eyes and teeth, but for whatever reason the idea that I might have a sinus problem never crossed his mind. He would load me up on vicodin under the assumption that I had cluster headaches. After finding that they didn't fully alleviate the pain, but wanted to take them anyway, I threw them away to avoid becoming an addict, and switched to ibuprofen and benadryl, which combined to make life tolerable.&lt;/p&gt;&lt;p&gt;During the next famliy get-together in North Carolina, I had an attack, and on the way back took a dose of my mother's benadryl, which was, in hindsight, a much stronger dose than my body was used to taking. I was driving her and 7 year old Stacey back to Ohio, and progressively getting more drowsy.&lt;/p&gt;&lt;p&gt;After passing Wytheville, Virginia, we came to the Big Walker Mountain Tunnel, a quiet, dark oasis from the noisy, bright freeway. After passing through it's 4/5ths of a mile, I was lulled nearly to sleep, and my foot kept getting heavier on the accelerator. I exited the tunnel going about 70 (55 is the speed limit from about 1/4 mile before to 1/4 mile after the tunnel), to the waiting speed trap on the other side.&lt;/p&gt;&lt;p&gt;Seeing my speedometer be nowhere near where I thought it was, combined with the flashing lights, I got a jolt of adrenalin, counteracting the diphenhydramine-induced coma I was heading towards. I uneventfully signed for my ticket, drove the remainder of the trip fully awake, and wrote my speeding users-fee check a few days later.&lt;/p&gt;&lt;p&gt;24. At 37, I fell victim to a stop sign trap at the corner of College ave. and Nicole in Westerville. When I first moved to Westerville, the stop sign was one street to the west, at the corner of College ave. and College ct., where it was immediately adjacent to the Middle School. A local petition was circulated for a ballot initiative to move the stop sign to Nicole, which has more houses on it. With the sign moved, the kids can immediately cross College from Nicole, then walk a block to their school, instead of walking down College for a block, then crossing immediately to their school. This is another reason why the rest of the world shakes their head at us and the obsession we have with our first-world problems.&lt;/p&gt;&lt;p&gt;Anyway, I drive past that stop-sign on the way to the freeway in the morning about an hour after the Middle School classes start, and until then I had just slowed down and looked for oncoming cars or kids late for school, and coasted through the stop sign at a slow crawl. Westerville posted a crusier down Nicole far enough to not be recognized as a threat by folks like myself who coasted through the stop sign, and I'm assuming pulled over about 20 people that day, netting the city an easy $2 grand.&lt;/p&gt;&lt;p&gt;After being pulled over, the officer asked me "Do you know why I pulled you over?". "Yes," I replied, and then bit my tongue to avoid blurting out any smartass comments about it being the end of the month and time for local police to "pay the rent", or my being more concerned with where pedestrians were than hidden patrol cars.&lt;/p&gt;&lt;p&gt;My ticket was over $100, due a week later. I hate the suburbs.&lt;/p&gt;&lt;p&gt;25. At 38, a friend and I drove up to Cleveland to see a band whose bassist we went to High School with. On the way back, my friend had to pee, so we pulled over on the freeway and he started to head to the bushes. A state trooper pulled over then, and asked us if everything was alright. "I'm sorry, officer, I just really need to pee," says my friend. The officer replies "alright, then, just making sure your car wasn't broken down."&lt;/p&gt;&lt;p&gt;I'm pretty sure it's a violation of some sort to stop on the freeway to pee in the bushes, but the trooper was less concerned about that than the possibility that someone might need help.&lt;/p&gt;&lt;p&gt;26. At 40, again with the expired tags. This time I was just heading to the grocery store with my pregnant wife, and driving "safely" - in my opinion, I always drive safely; this time I just wasn't breaking any traffic laws. The officer who pulled me over let me off with a warning in return for a promise to renew the tags soon.&lt;/p&gt;&lt;p&gt;So what can we make of all this? How could I have had all these encounters with cops and not been victimized, arrested, pepper-sprayed, cuffed, been threatened with dogs, or subjected to searches without probable cause? Well, being a white male probably skews things in my favor, I'll admit, but 26 times all ending without incident? Something else is at play.&lt;/p&gt;&lt;p&gt;Maybe the way I behave matters. I give cops respect and cooperation as a rule, and knowing that they quickly divide people into the "might kill me" and "probably won't kill me" categories, I try an awful lot to look like the latter, especially after my wakeup call when I was 18 and not thinking about how rooting around in a glove box might look. They do have jobs that put them in harm's way, obviously, and getting home without injury is going to weigh on them stronger than what I think my constitutional rights are. A pragmatic rule of thumb is cooperate on the street, object in the courtroom. In my case, doing much of the former has meant I needed to do none of the latter. Your mileage may vary.&lt;/p&gt;&lt;p&gt;I think what matters most, though, is that the negative stories you hear about corrupt police, OWS protestors getting smacked around, and adrenaline junkies with badges are mainly the exceptions to the rule. Most of the police are the good guys. If that weren't the case, I think I'd definitely have a different story to tell.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11885757-3799777866509520020?l=cautery.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://cautery.blogspot.com/feeds/3799777866509520020/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://cautery.blogspot.com/2011/12/encounters-with-police.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11885757/posts/default/3799777866509520020'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11885757/posts/default/3799777866509520020'/><link rel='alternate' type='text/html' href='http://cautery.blogspot.com/2011/12/encounters-with-police.html' title='Encounters with the police'/><author><name>Curtis Autery</name><uri>https://profiles.google.com/107677530285177731535</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh4.googleusercontent.com/--J3C_khtLqQ/AAAAAAAAAAI/AAAAAAAADT4/e2Qa5ncfAMk/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11885757.post-996800447069184932</id><published>2011-11-23T14:51:00.001-05:00</published><updated>2011-11-23T15:06:57.480-05:00</updated><title type='text'>Think of the children!!</title><content type='html'>&lt;p&gt;No seriously, think of the children. And not the bounty.&lt;/p&gt;&lt;p&gt;About a month ago, I listened to an NPR story on "All Things Considered" on my drive home from work, which concerned Native American children in South Dakota being declared "neglected" in high numbers, and being removed from their families and put up for adoption. The implication is that the South Dakota government was using a financial incentive as an excuse to go all Captain Pratt on the natives, killing the Indian and saving the man, by means of placing their kids in white homes. The text version of the NPR story is &lt;a href="http://www.npr.org/2011/10/25/141662357/incentives-and-cultural-bias-fuel-foster-system"&gt;available here&lt;/a&gt;.&lt;/p&gt;&lt;a name='more'&gt;&lt;/a&gt;&lt;p&gt;Some background on the NPR story:&lt;/p&gt;&lt;p&gt;In 1997, &lt;a href="http://www.gpo.gov/fdsys/pkg/BILLS-105hr867ih/pdf/BILLS-105hr867ih.pdf"&gt;HR 867&lt;/a&gt; was enacted by Bill Clinton, ammending section 473 of Part E (Federal Payments for Foster Care and Adoption Assistance) of title IV of the Social Security Act with some seemingly good-intentioned language, which had some disturbing consequences. Section 473a now contains this (ellipses, brackets, and sentence reformatting are mine):&lt;/p&gt;&lt;p&gt;&lt;i&gt;...the adoption incentive payment payable to a State ... shall be equal to the sum of $4,000, multiplied by the amount (if any) by which the number of foster child adoptions in the State during the fiscal year exceeds the base number of [same]; and $2,000, multiplied by the amount (if any) by which the number of special needs adoptions in the State during the fiscal year exceeds the base number of [same]&lt;/i&gt;&lt;/p&gt;&lt;p&gt;In 2008, the 110th Congress passed &lt;a href="http://www.fosteringconnections.org/tools/assets/files/Public_Law_110-351.pdf"&gt;Public Law 110–351&lt;/a&gt; (the "Fostering Connections to Success and Increasing Adoptions Act") which upped this last amount to $4,000, making the total take for getting a special needs kid adopted $8,000.&lt;/p&gt;&lt;p&gt;In basic English, one would assume "special needs" refers to children with mental retardation, epilepsy, behavioral problems, or other conditions requiring specialized care - basically kids that don't fit the happy suburban white couple Disney mold of the perfect kid to adopt. Title IV-E disagrees with that assumption, though. It's context of "special needs" implies only things that would make it harder to place a child with an adoptive family without a financial incentive. It states in section 473c:&lt;/p&gt;&lt;p&gt;&lt;i&gt;the child shall not be considered a child with special needs unless ... the State had first determined (A) that there exists with respect to the child a specific factor or condition (such as his ethnic background...)&lt;/i&gt;&lt;/p&gt;&lt;p&gt;This language basically invites states to declare all their black or native populations as special needs. A quick glance at the Child Welfare Information Gateway's &lt;a href="http://www.childwelfare.gov/adoption/adopt_assistance/questions.cfm?quest_id=1"&gt;Adoption Assistance by State page&lt;/a&gt; shows that many states have done just that. The two I want to focus on have been in the news recently for abuses by their Child Protective Services departments: South Dakota in regard to natives, and Virginia in regard to something a little more subtle.&lt;/p&gt;&lt;p&gt;South Dakota's categories for special needs:&lt;/p&gt;&lt;ul&gt;&lt;li /&gt;Eight years of age or older&lt;li /&gt;Race or religion (Native American)&lt;li /&gt;Member of a sibling group to be placed together for adoption&lt;li /&gt;Physical, emotional, neurological, or intellectual handicap or problem&lt;li /&gt;Need of a prosthesis, extensive, on-going, or anticipated medical care, or therapy for speech, physical, or psychological problems&lt;li /&gt;Adoption by foster parents with whom the child is living is the only appropriate permanency plan&lt;/ul&gt;And Virginia's:&lt;ul&gt;&lt;li /&gt;Six years of age or older&lt;li /&gt;Member of a minority race&lt;li /&gt;Member of a sibling group that is ready for placement at the same time and should not be separated&lt;li /&gt;A physical, mental, or emotional disability&lt;li /&gt;Hereditary tendency, congenital problem or birth injury resulting in a substantial risk of future disability&lt;li /&gt;A child with a physical, mental, emotional disability or hereditary tendency, congenital problem or birth injury present at the time of adoption but not diagnosed until up to one year after the final order is entered&lt;/ul&gt;&lt;p&gt;Does "birth injury" sound a little vague? Hold that thought.&lt;/p&gt;&lt;p&gt;Combine the $8,000 per head federal assistance with defining an entire race of people as harder to adopt, and you have just created a financial incentive to err on the side of removing minority children from their families.&lt;/p&gt;&lt;p&gt;The NPR story focussed on one particular group of kids who were seized from a family guilty of nothing more than poverty, and who had community and extended family childcare available. The story ends on sort of an up-note: A tribal council passed a resolution warning the state of South Dakota that criminal kidnapping charges would be filed if the kids weren't returned. They were returned, along with some snarky comments in the vein of "we can still come take your kids with impunity, so watch your step, Tonto." (The "Tonto" was silent, but implied.) The family of the kids is very worried by those comments, but to me they were an admission of defeat, the bully's retreating "I'll kick your ass" after getting knocked down by his intended victim.&lt;/p&gt;&lt;p&gt;For what it's worth, South Dakota &lt;a href="http://www.dakotahillslaw.com/wp-content/uploads/2011/11/2011-11-02-NPR-report-follow-up.pdf"&gt;responded to the NPR story&lt;/a&gt;, basically declaring their innocence and good intentions, falling just shy of espousing the "white man's burden".&lt;/p&gt;&lt;p&gt;I thought that was the end of it, some Black Hills comeuppance by natives who have been systematically harassed and disenfranchised by whites for centuries, and finally landed a good blow to them. But it isn't. A seemingly unrelated scandal is going on currently in Virginia, and was &lt;a href="http://washingtonexaminer.com/opinion/columnists/2011/11/federal-judge-should-hear-arlington-cps-case"&gt;publicly called out&lt;/a&gt; in the Washington Examiner.&lt;/p&gt;&lt;p&gt;In Baby Sabrina's case, there was enough going on to make it a case worth a quick peek by Virginia CPS, namely a diagnosis of "failure to thrive". My reading of the issue on the &lt;a href="http://www.kitandnancy.com/"&gt;mom's website&lt;/a&gt; is that Nancy was having trouble breastfeeding. The baby lost weight after birth, which all kids do, but did not then regain the lost weight, and in fact over the next weeks lost an additional couple of ounces. Sabrina was admitted to the hospital, fed a lot of formula, and then started gaining weight.&lt;/p&gt;&lt;p&gt;CPS investigated, which they were well within their right to do. Losing weight immediately after birth is par for the course, but a healthy baby who isn't being neglected should recover soon. Sabrina didn't. When CPS investigated, the correct response should have been "thanks for being concerned about our baby. We're trying hard, and now the doctors are helping, and we'll comply with their recommendations because we want nothing more than for Sabrina to be healthy." What they said instead was "fuck you, we're getting a lawyer," which raised the ire of the CPS.&lt;/p&gt;&lt;p&gt;From there, I would have expected a quick resolution, possibly with CPS backing off, or possibly with them temporarily placing the baby with Nancy's mom. Instead, they removed the baby from the family, and put it up for adoption. Why? Two reasons. First, because you don't stand up to the man; he's a spiteful son of a bitch. Second, the "failure to thrive" diagnosis fits well with the Virginia special needs category of "birth injury resulting in a substantial risk of future disability".&lt;/p&gt;&lt;p&gt;In my opinion, and I have nothing to back this up other than a gut feeling, Sabrina's possible status as special needs caused Virginia CPS to side with removal and adoption outside the family, rather than just monitoring or placing her with grandma. Even though the state's legal costs quickly surpassed the $8,000 take. Nancy's site does have a good deal of atrocity propaganda on it, and it's hard to read through that and find the true story, but there are enough questions about the case that it should at least be heard by a federal judge. Maybe they're full of crap and were truly neglectful, or maybe Virginia CPS overstepped, but either way they should get their day in court.&lt;/p&gt;&lt;p&gt;Maybe neither the South Dakota nor Virginia cases are caused by official state evil. Maybe the intentions are always good, with an eye on protecting children and giving them the best homes possible, and after seeing enough bad in the truly neglectful parents, CPS case workers see bad in all parents. I don't know, but I do know that there is a conflict of interest for any state that accepts per-child adoption bounties. Yes, a little rhetorical, but a bounty is what it is. The spirit of the law is to use the money when standard attempts to place the child have failed, but in Sabrina's case as with the South Dakota case, an acceptable placement was available and ignored. So there is at least the perception, a revealed preference, that underfunded state CPS agencies now have an eye on potential federal money, and skew their cases accordingly.&lt;/p&gt;&lt;p&gt;No matter the true nature of the South Dakota and Virginia cases, adding this motivation for funding into organizations tasked with removing children from their homes is a powderkeg, especially in today's world of protesters fighting the police, unemployment bringing children into poverty, and the simple fact that parents who feel their children are threatened are irrational, more likely to put up their dukes than to investigate the proper appeals process. The 473a ammendment to Title IV-E will be responsible for needlessly breaking up families, and for police, social workers, parents, or children getting injured or worse. And it needs to be repealed.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11885757-996800447069184932?l=cautery.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://cautery.blogspot.com/feeds/996800447069184932/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://cautery.blogspot.com/2011/11/think-of-children.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11885757/posts/default/996800447069184932'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11885757/posts/default/996800447069184932'/><link rel='alternate' type='text/html' href='http://cautery.blogspot.com/2011/11/think-of-children.html' title='Think of the children!!'/><author><name>Curtis Autery</name><uri>https://profiles.google.com/107677530285177731535</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh4.googleusercontent.com/--J3C_khtLqQ/AAAAAAAAAAI/AAAAAAAADT4/e2Qa5ncfAMk/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11885757.post-7517359464595946055</id><published>2011-10-30T16:17:00.001-04:00</published><updated>2011-10-30T16:20:00.249-04:00</updated><title type='text'>Tinkering with Calculus</title><content type='html'>A little document I whipped up on Google Docs after dusting off a calculus book: &lt;br/&gt;&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;&lt;iframe src="https://docs.google.com/document/pub?id=1K9JFgwmG9uP56CbCaMWDsEICWVk01ZB99tBcFbyP3P4&amp;amp;embedded=true" frameborder=0 style="width:100%;height:100%"&gt;&lt;/iframe&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11885757-7517359464595946055?l=cautery.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://cautery.blogspot.com/feeds/7517359464595946055/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://cautery.blogspot.com/2011/10/tinkering-with-calculus.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11885757/posts/default/7517359464595946055'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11885757/posts/default/7517359464595946055'/><link rel='alternate' type='text/html' href='http://cautery.blogspot.com/2011/10/tinkering-with-calculus.html' title='Tinkering with Calculus'/><author><name>Curtis Autery</name><uri>https://profiles.google.com/107677530285177731535</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh4.googleusercontent.com/--J3C_khtLqQ/AAAAAAAAAAI/AAAAAAAADT4/e2Qa5ncfAMk/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11885757.post-6841478193942933347</id><published>2011-10-16T13:08:00.001-04:00</published><updated>2011-10-18T12:10:32.455-04:00</updated><title type='text'>Adelaide</title><content type='html'>&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/-CYQialjHMtQ/TprlvwnJ2MI/AAAAAAAADUo/utzKCWKGbBA/s1600/adelaide.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="240" src="http://1.bp.blogspot.com/-CYQialjHMtQ/TprlvwnJ2MI/AAAAAAAADUo/utzKCWKGbBA/s320/adelaide.jpg" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;p style="text-align: center;"&gt;Adelaide Lenora Allerding&lt;br/&gt;October 14, 2011 4:20am&lt;br/&gt;6lbs 12oz, 19.5"&lt;/p&gt;&lt;a name='more'&gt;&lt;/a&gt;&lt;p&gt;As I mentioned a while back, my wife and I decided to have a baby. It's an easy time for us to have a baby, because I have a stable job, I'm still young enough and healthy enough, our marriage is strong, Liberty and I agree on most things, and Liberty has a strong support network of family and friends. It's a hard time for us to have a baby because I am no longer happy working corporate IT, or living in Ohio, Liberty is unemployed and has a mountain of student loan debt, the house is falling apart, and although I'm healthy now at 40, I'm worried about what my health will be in 10 years at 50. The more I dwell on these negatives, in fact, the grayer my hair seems to get.&lt;/p&gt;&lt;p&gt;In our shared opinion, the easy outweighed the hard, but if we didn't think that was so, we probably would have had a baby anyway. Because we both love babies, and each other, and, you know... come at me, bro. Just bring it, world. Rain some more bad on us and see who's still standing tomorrow.&lt;/p&gt;&lt;p&gt;Liberty was originally due on October 12, according to the pregnancy wheel used by our CHOICE midwife, Audra. We planned to drive down to "The Farm" in Tennessee, home to Ina May Gaskin, and rent one of their birthing cabins for a month and have the baby there. (Even though we are the 99%, as the protest saying goes, and it would have taken more cash than I could comfortably afford, with no guarantee that insurance would cover any of it -- see "come at me, bro" above.) We would see local midwives here at the CHOICE center, and when she seemed close to ready for labor, we would truck on down south to spend a month on The Farm. That was the plan, anyway.&lt;/p&gt;&lt;p&gt;Throughout the pregnancy, Liberty had stomach pains that were pretty severe. She was diagnosed as having H. Pylori, easily treated with some hard core antibiotics whose side effects include birth defects and miscarriages. So instead she suffered the entire pregnancy, nursing her way through the pain with probiotics, red raspberry/nettle tea, and other herbal stomach aids.&lt;/p&gt;&lt;p&gt;Whether a direct result of the stomach problem or coincidental to it, the baby was the wrong size late in the second trimester. The midwives weren't sure if the due date was correct, if a lull in growth was baseline for how Liberty made babies, or if there was a nutrition or development problem. So we did what we had been trying explicitly to avoid: started medicalizing the pregnancy. We got a few ultrasounds over the next month, Liberty started making appointments at the Department of Health for various tests and labwork, and The Farm dropped us as clients.&lt;/p&gt;&lt;p&gt;The last was a bad blow, and Liberty was inconsolable for a few days. "I tried to do everything right," she said during the worst of it. A guy needs to fix things, it's just how we're made, and I could do nothing to either attack the problem or comfort my wife. I still get a little choked up when I think about it.&lt;/p&gt;&lt;p&gt;CHOICE kept us as clients, and the baby made up for lost time over the next few weeks, due either to providence's original plan, or Liberty forcing down food she knew was going to make her feel like hell in a couple hours. As the original due date approached, the baby was the right size, no problems were indicated on ultrasound or blood tests, and we planned for a home birth. Audra and another midwife, Amy, came for a home visit, gave us a kit of baby making stuff including pads to catch blood and stuff, instructions I was to memorize in case we couldn't get help in time ("if the umbilical cord seems looped around the neck..." I was really hoping it wouldn't come down to me being Liberty's only help in delivery), and the ominous infant resuscitation board. Just in case.&lt;/p&gt;&lt;p&gt;Just imagine living with your pregnant wife for a couple months with an infant resuscitation board in your bedroom. Just think about that for a minute.&lt;/p&gt;&lt;p&gt;On Thursday, October 13 at about 10pm, Liberty's behavior changed, and her contractions were visibly making her more uncomfortable than usual. (For those not in the know, pregnant women have contractions pretty much all the time in the third trimester. Not constantly, but most days there will be at least a few, growing in regularity and duration. When this happens, the stomach gets rock hard. It's pretty wild.) Anyway, I figured this was it, so I went down and made some coffee, got re-dressed, and started pitching the idea of calling Audra.&lt;/p&gt;&lt;p&gt;At 11, Liberty made the first call. With a midwife now on standby, Liberty tried to take a warm bath to see if the contractions would pass. I drank my coffee, and went in to check on Scout. She was awake, as we were making plenty of noise. "I think your mom's in labor," I said to her, "this can take a long time, so try to get some rest, and I'll come wake you up if anything interesting happens." Naturally she didn't sleep any more, nor, I'm pretty sure, did she try to.&lt;/p&gt;&lt;p&gt;The bath didn't help. Liberty's pain level got worse, and the contractions got stronger. I made the second call to Audra at about 12:15, as Liberty was reduced to communicating through head nods and shakes. "I think we need you," I said, and hearing Liberty's cries in the background, she said she would get ready and come over. She showed up about a half hour later, and Amy shortly after that.&lt;/p&gt;&lt;p&gt;At about 1:30, I could hear Scout rustling around, and I checked on her again. She wanted to come help. Liberty and the midwives were all OK with her coming in, so she did. Throughout the labor, little Scout was ..and again, I'm choking up just thinking about it... the absolute best that humanity has to offer. She watched quietly, whispered a few questions to Amy, held mom's hand and patted her hair, ran to get water or juice when mom was thirsty, and held it to her mouth so she could drink from the straw if she didn't have a free hand to hold her own cup. I told her later how proud I was of her, and I swear I felt as much love for her then as I did when little Stacey explained to me how the sunset was medicine for the sky.&lt;/p&gt;&lt;p&gt;It was a little scary for Scout, watching her mom in pain, and I geared up to give her the following consoling speech: "Angel, do you see how the midwives faces are all calm and relaxed? They've seen a lot of deliveries before, and they know this is all normal. See how they don't looked worried?" Before I started to say that, though, I looked at their faces. I saw something hiding behind the relaxed expressions. Concern. Worry. The feeling that something isn't right. The problem was that everything seemed to be going normally, but Liberty was in much more pain than she should be. So I withheld my reassuring speech, and waited for the problem to reveal itself.&lt;/p&gt;&lt;p&gt;It did so very soon thereafter. Liberty became completely dilated and effaced, and as soon as she started pushing, the baby's heartbeat dropped to about 80, by my estimate. A midwife pitched her voice low and said softly to the other "that's too low." They gave Liberty oxygen, told her to lie on her left side (I think it was the left side), and try not to push for a minute. The baby's heart rate came back up, but not quite to where it should have been. The next push, the problem was still there. "We might need to go to the hospital," a midwife said.&lt;/p&gt;&lt;p&gt;I put on my socks and shoes, got some socks for Scout and told her to go put them on with her boots, and I ran down to get her a coat. By then, the plan was definite: we would leave for the hospital immediately. Liberty and Amy would ride in the back of my car, Audra would call the hospital, canvas the house, and follow. I helped Liberty, now in pain, scared, and confused ("what's happening?!" a near sob, "what are we doing?") out of the house to the car. Amy barked an order to remove Scout's car seat, which I did, and directed Liberty to get on her hands an knees in the back, which she did. Amy got in beside Liberty and reaffixed the oxygen mask, I got Scout buckled into her car seat in the front, made sure everyone was ready...&lt;/p&gt;&lt;p&gt;And I put the God-damned hammer down.&lt;/p&gt;&lt;p&gt;It was 4 in the morning, and the streets from my house to St. Ann's were nearly vacant. We pulled up to the hospital about 5 minutes after we left the house, including two blown stop signs, a left turn on red, and a 90 in a 45.&lt;/p&gt;&lt;p&gt;Amy went in to get a wheelchair, and I got Liberty out and started walking her to the door. Scout followed along, and had an almost pleasant look to her. Very calm, inquisitive even. "What's all this crazy driving and running to the hospital all about?" The ladies went in together while I parked the car, and by the time I made it in the hospital everyone was gone. I had a brief conversation with the security guard:&lt;/p&gt;&lt;p&gt;"Hey, there was a pregnant woman in a wheelchair..."&lt;/p&gt;&lt;p&gt;While pointing to the elevator, "Second floor."&lt;/p&gt;&lt;p&gt;I got off on the second floor, which on first glance was a ghost town. The receptionist's desk was empty, and the security doors allowing one back to the delivery rooms were closing. I dashed to them and grabbed one before it closed, and slipped into the secure area. I heard the sounds of people around a corner to my right, and headed over that way purposefully, damned near storming up to the nurse's station. "LIBERTY ALLERDING!" If you know me at all, you know that this is completely out of character for me. Yelling at people at a nurse's station is as unlikely a thing to happen as me pushing people out of the way at Disney World... which I also did when I realized 6 year old Stacey wasn't beside me any more. Issues with my kids tend to bring to the surface that testosterone response I'm always suppressing. &lt;/p&gt;&lt;p&gt;Anyway, before I leapt the desk and started rifling through charts, a very nice and polite lady told me where she was, and asked me first to come to the reception area to give them my insurance card, date of birth, and whatnot. She took the information quickly, and I went to the delivery room and caught the last bit of the action: My wife in exactly the position she had been trying to avoid for the entire pregnancy: surrounded by doctors, on a delivery table, legs in stirrups.&lt;/p&gt;&lt;p&gt;Strangely, the delivery at this point was completely uneventful. "Push!",  "OK, I have the head, one more!", "Waah!". Adelaide was delivered naturally, all things considered, at 4:20am, a fact that will give all the heads she meets no end of giggles.&lt;/p&gt;&lt;p&gt;The St. Ann's staff was everything I thought they wouldn't be. They were polite, they took our direction (no episiotomy, leave the baby with us instead of taking it to the nursery), they waited for Adelaide to pink up before clamping the umbilical cord, and only offered token resistance when Liberty said not to stitch her tears up. Despite being lit up with testosterone and unable to keep my legs from shaking as I watched, I only said one cross thing to the staff: "She said no, leave it at that."&lt;/p&gt;&lt;p&gt;A side note here: This isn't my story, and while I'm writing it from my point of view, I don't intend for this to look like it was all about me. This is Liberty's story, slowly pushed off the course she had for a pregnancy and labor experience, but nonetheless emerging victorious. She did all the suffering, and my actions, despite their comedic value and reflection of how much I care for Liberty, could have been performed by a chauffeur  just as easily. And a personal assistant. And a bodyguard. But still, her story. I'm not the hero, I can just only report what I saw.&lt;/p&gt;&lt;p&gt;We asked to be released from the hospital as soon as we could, which we thought at first was going to involve staying at least 24 hours for observation since the labor was problematic. At this point we realized that Liberty and Scout were wearing nightgowns, and Liberty didn't have her purse or shoes, and no one had a toothbrush. And little Adelaide had no clothes and her car seat was disassembled at the house, since we didn't expect to need it for a while. So I drove home, obeying many more traffic laws than the trip to the hospital, packed up a bag of stuff, put the car seat together, and came back to find that we were being released.&lt;/p&gt;&lt;p&gt;Adelaide's responses were all normal (I think someone said "8, 9, and 9", referring to some reflex/awareness test), the delivery was vaginal with no cutting, and mom was coherent, so there was no reason to keep us. Another plus for St. Ann's in my book: "You seem OK, take off and let us know if you need anything." So, good on them for being more supportive of moms and less interventionist. And good on the ladies at CHOICE who were humble enough to see when more help was needed, but strong enough to continue to advocate for Liberty at the hospital.&lt;/p&gt; Naturally I'm proud of Liberty for enduring both  monumental suffering and indignity, and little Adelaide for hanging in there and coming out healthy. But when I think back over the whole affair, my strongest memory is of Scout and a cup of juice held out to her mom, the fearlessness when things started to go wrong, and the excitement at getting to meet her sister.&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/-rVqGiHBP-Kk/Tprl0nt8ugI/AAAAAAAADUw/A5J0KR0WUDE/s1600/IMG_5540.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="320" src="http://4.bp.blogspot.com/-rVqGiHBP-Kk/Tprl0nt8ugI/AAAAAAAADUw/A5J0KR0WUDE/s320/IMG_5540.JPG" width="240" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11885757-6841478193942933347?l=cautery.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://cautery.blogspot.com/feeds/6841478193942933347/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://cautery.blogspot.com/2011/10/adelaide.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11885757/posts/default/6841478193942933347'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11885757/posts/default/6841478193942933347'/><link rel='alternate' type='text/html' href='http://cautery.blogspot.com/2011/10/adelaide.html' title='Adelaide'/><author><name>Curtis Autery</name><uri>https://profiles.google.com/107677530285177731535</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh4.googleusercontent.com/--J3C_khtLqQ/AAAAAAAAAAI/AAAAAAAADT4/e2Qa5ncfAMk/s512-c/photo.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/-CYQialjHMtQ/TprlvwnJ2MI/AAAAAAAADUo/utzKCWKGbBA/s72-c/adelaide.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11885757.post-5439340503750743643</id><published>2011-04-22T11:48:00.000-04:00</published><updated>2011-04-22T11:48:46.962-04:00</updated><title type='text'>Designing a Notes iPhone web app</title><content type='html'>&lt;blockquote&gt;"A pedagogical decision hides behind every design decision" -Dan Meyer&lt;/blockquote&gt;&lt;p&gt;For the last few weekends, I've been coding in the early mornings while my wife sleeps beside me, working on a replacement for the broken built-in Notes app on her iPhone. My replacement is usable and stable at this point, and I'm just putting the final polish on the code to remove duplication, improve comments, delete orphan code, etc.&lt;/p&gt;&lt;p&gt;The tools I used to design this are the same as the &lt;a href="http://lentiltracker.appspot.com"&gt;diet tracker app&lt;/a&gt; I wrote for her to track her protein and calcium during her pregnancy: handwritten JavaScript, and Google's appspot.com webserver to properly serve the cache manifest's content type. This was initially going to be a quick hack to learn the API for Web SQL Databases, so I could retrofit database support in the diet tracker, but along the way I learned a lot about designing an HTML Notes application, which was more involved than I anticipated.&lt;/p&gt;&lt;p&gt;This post recounts some of the lessons I learned, and the decisions I made between competing solutions that each had pluses and minuses. Some screenshots of the current product are available below the fold.&lt;/p&gt;&lt;a name='more'&gt;&lt;/a&gt;&lt;p&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/-Yl-lncVUyHY/TbGib0gqYWI/AAAAAAAADRA/scnJ9-vENl8/s1600/notesIconScreenshot.png" imageanchor="1" style=""&gt;&lt;img border="0" height="320" width="214" src="http://1.bp.blogspot.com/-Yl-lncVUyHY/TbGib0gqYWI/AAAAAAAADRA/scnJ9-vENl8/s320/notesIconScreenshot.png" /&gt;&lt;/a&gt;&lt;/div&gt;Custom icon on iPhone home screen.&lt;/p&gt;&lt;p&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-TdZzpIv17RI/TbGion2hm2I/AAAAAAAADRI/0r_kCgj-tJ8/s1600/notesInputScreenshot.png" imageanchor="1" style=""&gt;&lt;img border="0" height="320" width="214" src="http://3.bp.blogspot.com/-TdZzpIv17RI/TbGion2hm2I/AAAAAAAADRI/0r_kCgj-tJ8/s320/notesInputScreenshot.png" /&gt;&lt;/a&gt;&lt;/div&gt;Editing: The "back" button on the menu bar is three rectangles and a circle drawn using the HTML5 canvas spec. More on that below.&lt;/p&gt;&lt;p&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/-7Or7TtG0KBY/TbGiwGIBBYI/AAAAAAAADRQ/gquT1po7l50/s1600/notesListScreenshot.png" imageanchor="1" style=""&gt;&lt;img border="0" height="320" width="214" src="http://1.bp.blogspot.com/-7Or7TtG0KBY/TbGiwGIBBYI/AAAAAAAADRQ/gquT1po7l50/s320/notesListScreenshot.png" /&gt;&lt;/a&gt;&lt;/div&gt;List: Normally all those minus signs won't be there. The red minus sign at the very top is twisted to indicate being in delete mode. Then each note gets its own minus sign, and twisting them unlocks a delete button. Again, more on that below.&lt;/p&gt;&lt;p class=section&gt;Impetus&lt;/p&gt;&lt;p&gt;The story starts a few years ago when the first iPhone came out. I bought one for Liberty, and it quickly became the source of many pictures of Scout, and of our adventures - pictures that we didn't want to lose. Because of the fear of losing these images, we rarely synced the phone with iTunes, always backing the images up first, and fearing the bad things that would happen when newer iOS versions were installed.&lt;/p&gt;&lt;p&gt;During syncing, we've lost music, saw the built-in applications get slower, had mysterious timeouts backing up data, but fortunately have never lost any of the images. Since each upgrade seemed to make the whole system slower, and assuming newer OS versions would need even more horsepower, syncing again would make the phone virtually unusable.&lt;/p&gt;&lt;p&gt;"Why not upgrade to an iPhone 4" you ask? I've pitched this idea to Liberty, which she soundly dismissed. Her reason: the newer iPhones look more boxy, whereas the original looks curvy and sleek. Well, there's no arguing with that logic... or at least, I don't really know where I'd start, so there's nothing for it but to help her use the phone she has.&lt;/p&gt;&lt;p&gt;The current state of her phone is iOS 3.1.3 with the version of Safari using WebKit engine 528.18. Theses specific versions have some anomalies, namely the Notes app tends to break during the upgrade (file permission problems), and Safari's HTML5 support had some minor issues that have since been ironed out (such as placeholders on textarea fields, which I'll get into later).&lt;/p&gt;&lt;p&gt;The Notes app breaking was the real impetus of this project. Our symptoms consist of the app opening fine, but clicking on most of the notes results in the app simply closing. Only one note opens, but trying to alter it also results in the app closing. There are some suggestions for fixes online, which mainly involve monkeying around with the Settings app and trying again, but those haven't worked for us.&lt;/p&gt;&lt;p&gt;Unix hacker that I am, I could jailbreak the phone, install an SSH daemon, then connect from my PC and do some commandline chmods on the note files, but Liberty didn't like the idea of me hacking her phone like that. One of the things that is helping the two of us have a successful marriage is my not fixating on things like this like I would have a decade ago. If she wants an older phone with an unaltered OS, then that's the environment I work in. If the puzzle is hard to solve under those conditions, so much the better: the puzzle is the fun part. Solving the puzzle is the main focus, not advocacy for being modern or edgy (whatever that means).&lt;/p&gt;&lt;p&gt;When I finished the diet tracker app enough to be usable, I looked for another app to write to learn web databases, and the broken Notes app was the obvious candidate. And so, I began.&lt;/p&gt;&lt;p&gt;My &lt;a href="http://cautery.blogspot.com/2011/04/rage-against-pitocin-cesarean-complex.html"&gt;previous blog entry&lt;/a&gt; on the diet tracker web app shows (along with my radical views on childbirth) some basics of using offline application caches, and how to turn a website bookmarked to the iPhone home screen into something that behaves like a native application, so I won't dwell on those points here. Let's start instead with databases.&lt;/p&gt;&lt;p class=section&gt;Web Databases&lt;/p&gt;&lt;p&gt;There are two basic flavors of JavaScript accessible local databases: the &lt;a href="http://www.w3.org/TR/webdatabase/"&gt;Web SQL Database&lt;/a&gt;, which is supported in WebKit browsers (Chrome and Safari), and the &lt;a href="http://www.w3.org/TR/IndexedDB/"&gt;Indexed Database API&lt;/a&gt;. The latter was proposed by Oracle a couple years ago, and currently the W3C is declaring Web SQL a deprecated standard. The newer standard has been adopted by Firefox 4, so Wikipedia tells me, as well as Chrome 11 (meaning support for it should be rolled back into WebKit in the near future, propagating to Safari and Chromium). For now, though, Web SQL is what's available to me for iPhone web apps, and that is what I used.&lt;/p&gt;&lt;p&gt;In Web SQL, the method window.openDatabase() returns a handle to a database object. You pass that method a few variables - the local and friendly names of the database, its version number, and the number of bytes that should be allocated for it - and the browser uses a built-in instance of SQLite to attempt to find or create the database.&lt;/p&gt;&lt;p&gt;A SQL transaction function, (transaction handle).executeSql(), can be created that specifies the SQL to execute, any variables to substitute for question marks in the SQL, and asynchronous functions to call on the success or failure of the SQL statement.&lt;/p&gt;&lt;p&gt;The database handle and the transaction function are married together with an odd construct, resulting in code that can look like this:&lt;/p&gt;&lt;pre&gt;&lt;br /&gt;var db = openDatabase('etchnotes', '1.0', 'etchnotes', 200000);&lt;br /&gt;db.transaction(function(t){&lt;br /&gt;  t.executeSql('SELECT rowid, name FROM notes', [], function(tx, result) {&lt;br /&gt;    var firstName = result.rows.item(0).name;&lt;br /&gt;    // ...do other stuff with results;&lt;br /&gt;  });&lt;br /&gt;});&lt;br /&gt;&lt;/pre&gt;&lt;p&gt;First, that's too much abstraction and punctuation for my taste; without a lot of familiarity with constructs like the above, it's hard to parse, and harder still find syntax errors in it. But such is the world of asynchronous callbacks.&lt;/p&gt;&lt;p&gt;A slightly better construct is this:&lt;/p&gt;&lt;pre&gt;&lt;br /&gt;var db = openDatabase('etchnotes', '1.0', 'etchnotes', 200000);&lt;br /&gt;function onSuccess(tx, result) { ...stuff... }&lt;br /&gt;function onFailure(tx, result) { ...stuff... }&lt;br /&gt;&lt;br /&gt;db.transaction(function(t){&lt;br /&gt;  t.executeSql('SELECT rowid, name FROM notes', [], onSuccess, onFailure);&lt;br /&gt;});&lt;br /&gt;&lt;/pre&gt;&lt;p&gt;That reads a lot more clean to me, but I still don't like that the db object can't take SQL directly. To address that, I created a closure function that has a runSql method, opens and initializes the database, and manages its own transactions and error callbacks. This is a simplified version of the closure:&lt;/p&gt;&lt;pre&gt;&lt;br /&gt;function getNotesDB() {&lt;br /&gt;  var db = openDatabase('etchnotes', '1.0', 'etchnotes', 200000);&lt;br /&gt;  var errorHandler = function(tx, e) { alert("Database error\n" + e.message); }&lt;br /&gt;&lt;br /&gt;  this.runSql = function(sql, callback) {&lt;br /&gt;    db.transaction(function(t){&lt;br /&gt;      t.executeSql(sql, [], callback, errorHandler);&lt;br /&gt;    });&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  return this;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;p&gt;And calling it is a breeze:&lt;/p&gt;&lt;pre&gt;&lt;br /&gt;var dbClosure = getNotesDB();&lt;br /&gt;dbClosure.runSql("select rowid, name from notes", onSuccess);&lt;br /&gt;&lt;/pre&gt;&lt;p&gt;After a little exposure, I was able to get my head around async database work, and splitting functions into two parts: the click handler that submits a SQL query, and the callback function that parses the results. I won't dwell on that any more here, but the source code is available below.&lt;/p&gt;&lt;p class=section&gt;Rotating an HTML5 canvas&lt;/p&gt;&lt;p&gt;A standard design pattern on the iPhone is unlocking a delete button. Rather than each removable item having a delete button always present, and an "are you sure?" popup to deal with, some iPhone apps have an edit mode that provides each item with a minus sign on the left. Touching that makes it rotate counter-clockwise, and exposes a hidden delete button for that item. Click the delete button, and the app assumes you knew what you were doing and deletes the item with no verification. Or click the minus button again, and it rotate back to where it was, hiding the delete button. Intuitive and neat, typical of Apple's UI design, and hard to find fault with.&lt;/p&gt;&lt;p&gt;Implementing a similar minus button rotation with JavaScript took a little research, but ultimately wasn't very difficult. The two basic methods I chose between were CSS transforms using something like:&lt;/p&gt;&lt;pre&gt;&lt;br /&gt;minusButton.style['-webkit-transform'] = "rotate(30deg)";&lt;br /&gt;&lt;/pre&gt;&lt;p&gt;...and HTML5 canvas context rotations, for example&lt;/p&gt;&lt;pre&gt;&lt;br /&gt;var ctx = canvas.getContext('2d');&lt;br /&gt;ctx.rotate(angle);&lt;br /&gt;ctx.fillRect(x, y, w, h);&lt;br /&gt;&lt;/pre&gt;&lt;p&gt;I chose the latter, choosing also to not use image files, but instead to draw the button objects on canvases using paths (think GeneralPath from Java2D). Since all I needed was a circle and a line for a minus button, this was pretty trivial. While rotating the canvas was easy after I figured out to use ctx.translate() to move the origin point to the center of the image, animating the rotation was a little more involved.&lt;/p&gt;&lt;p&gt;I may have been better off using a CSS transition, but having already headed down the path of canvases, I stuck with it, ending up with some arcane looking infrastructure around canvases and their states. Behold the Spinner Factory:&lt;/p&gt;&lt;pre&gt;&lt;br /&gt;function SpinnerFactory(size) {&lt;br /&gt;  var cr = Math.PI*2;        // Circle&lt;br /&gt;  var deg = Math.PI/180;     // One degree&lt;br /&gt;  var scale = size/100;&lt;br /&gt;&lt;br /&gt;  function getScaledContext(obj) {&lt;br /&gt;   // Get canvas context&lt;br /&gt;    var ctx = obj.getContext('2d');&lt;br /&gt;&lt;br /&gt;    if (!obj.scaled) {&lt;br /&gt;      ctx.scale(scale, scale);&lt;br /&gt;      ctx.translate(50, 50);&lt;br /&gt;      obj.scaled = true;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    ctx.clearRect(-50, -50, 100, 100);&lt;br /&gt;    return ctx;&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  function drawMinus(obj) {&lt;br /&gt;    // Get scaled canvas context, and clear previous image&lt;br /&gt;    var ctx = getScaledContext(obj);&lt;br /&gt;  &lt;br /&gt;    // Red circle&lt;br /&gt;    ctx.fillStyle = 'red';&lt;br /&gt;    ctx.beginPath();&lt;br /&gt;    ctx.arc(0, 0, 50, 0, cr, true);&lt;br /&gt;    ctx.fill();&lt;br /&gt;  &lt;br /&gt;    // White minus sign, rotated to obj.angle degrees&lt;br /&gt;    ctx.save();&lt;br /&gt;    ctx.rotate(obj.angle * deg);&lt;br /&gt;    ctx.fillStyle = 'white';&lt;br /&gt;    ctx.fillRect(-29, -6, 58, 12);&lt;br /&gt;    ctx.restore();&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  function spinLeft(obj) { spin(obj, -15, -90); }&lt;br /&gt;  function spinRight(obj) { spin(obj, 15, 0); }&lt;br /&gt;  &lt;br /&gt;  function spin(obj, step, goal) {&lt;br /&gt;    // While spinning, disable object's click handler to prevent competing spins&lt;br /&gt;    if (obj.onclick) {&lt;br /&gt;      obj.deferredOnclick = obj.onclick;&lt;br /&gt;      obj.onclick = null;&lt;br /&gt;    }&lt;br /&gt;    if (step == 0) return;&lt;br /&gt;    if ((step &amp;lt; 0 &amp;amp;&amp;amp; obj.angle &amp;lt;= goal) || (step &amp;gt; 0 &amp;amp;&amp;amp; obj.angle &amp;gt;= goal)) {&lt;br /&gt;      obj.angle = goal;&lt;br /&gt;      if (obj.nextLeft) {&lt;br /&gt;        obj.nextLeft = false;&lt;br /&gt;        if (obj.unlockCbk != null) obj.unlockCbk();&lt;br /&gt;        obj.unlockObj.style.display = 'block';&lt;br /&gt;      } else {&lt;br /&gt;        obj.nextLeft = true;&lt;br /&gt;        if (obj.lockCbk != null) obj.lockCbk();&lt;br /&gt;        obj.unlockObj.style.display = 'none';&lt;br /&gt;      }&lt;br /&gt;      if (obj.deferredOnclick) {&lt;br /&gt;        obj.onclick = obj.deferredOnclick;&lt;br /&gt;        obj.deferredOnclick = null;&lt;br /&gt;      }&lt;br /&gt;      return;&lt;br /&gt;    }&lt;br /&gt;    obj.angle += step;&lt;br /&gt;    drawMinus(obj);&lt;br /&gt;    setTimeout(function() {spin(obj, step, goal)}, 30);&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  this.makeSpinner = function(className, unlockObj, lockCbk) {&lt;br /&gt;    unlockObj.style.display = 'none';&lt;br /&gt;    var cnv = document.createElement('canvas');&lt;br /&gt;    cnv.width = size;&lt;br /&gt;    cnv.height = size;&lt;br /&gt;    cnv.className = className;&lt;br /&gt;    cnv.angle = 0;&lt;br /&gt;    cnv.nextLeft = true;&lt;br /&gt;    cnv.unlockObj = unlockObj;&lt;br /&gt;    cnv.spinRight = function() { spinRight(cnv); }&lt;br /&gt;    cnv.onclick = function(e) {&lt;br /&gt;      cnv.nextLeft ? spinLeft(cnv) : spinRight(cnv);&lt;br /&gt;      e.stopPropagation();&lt;br /&gt;    }&lt;br /&gt;    if (lockCbk != null) cnv.lockCbk = lockCbk;&lt;br /&gt;    drawMinus(cnv);&lt;br /&gt;    return cnv;&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;p&gt;The Spinner Factory is a JavaScript closure that takes a single variable, size, which determines the diameter of the spinner buttons it creates. Individual spinners are created with the makeSpinner() method, which takes variables for the CSS class name for the spinner, the object to hide/unhide at the end of the spin, and an optional function reference to call after an object is re-locked. Typical usage would look like this:&lt;/p&gt;&lt;pre&gt;&lt;br /&gt;sf = new SpinnerFactory(30);&lt;br /&gt;item.appendChild(sf.makeSpinner('minusBtn', deleteBtn));&lt;br /&gt;&lt;/pre&gt;&lt;p&gt;...where "item" is a div tag you want to add a spinner to, and "deleteBtn" is the object that gets unhidden.&lt;/p&gt;&lt;p&gt;After getting all that working, I created some additional methods for the Spinner Factory to use the same scale to draw plus signs and arrows, which don't spin.&lt;/p&gt;&lt;p class=section&gt;Sizing a textarea to fit its content&lt;/p&gt;&lt;p&gt;This was a fun one! After taking great pains to tweak CSS settings to make the note text line up with the lines on the background image, I noticed during testing that if I entered too much text, I couldn't scroll to see it. To complicate matters, mobile Safari doesn't appear to use scrollbars, so there wasn't even an indication that there was more text to see.&lt;/p&gt;&lt;p&gt;After some online searching, I found that you can scroll textareas on the iPhone by using two-fingered swipes. I tested this, and although it worked, it was awkward, and transitions between edit mode (when the keyboard display pops up) and normal viewing mode left the display in unusual positions. I could struggle through it, but it wasn't ideal, and certainly not good enough for my wife.&lt;/p&gt;&lt;p&gt;There are DOM properties for textareas that can be monitored after each character is entered. The one that seemed most important was scrollHeight. It remained constant until any text overflowed off of the screen, then the value changed to the height needed by all the text. This was all independent of font size or the width of the textarea.&lt;/p&gt;&lt;p&gt;I quickly hacked out an event listener that monitored for scrollHeight changes, and set the textarea height to match. Since no scrollbars ever appeared, the user would have no indication that anything interesting was happening. The main problem with this is that the box wouldn't shrink back down if you deleted a bunch of text. The scrollHeight attribute doesn't go below the current height of the textarea. Although not necessarily a showstopper, I didn't like the idea that a user might think there was a lot of whitespace offscreen at the end of the note. I wanted a box that could grow and shrink with the text.&lt;/p&gt;&lt;p&gt;I switched instead to a method that uses a second textarea, hidden offscreen, to make measurements. First, all textareas in this app have some very specific CSS settings for positioning:&lt;/p&gt;&lt;pre&gt;&lt;br /&gt;textarea {&lt;br /&gt;  border: 0;&lt;br /&gt;  background-image:url('images/paper.png');&lt;br /&gt;  margin:0;&lt;br /&gt;  padding:0;&lt;br /&gt;  padding-left: 13px;&lt;br /&gt;  width: 307px;&lt;br /&gt;  font-size: 17px;&lt;br /&gt;  outline: none;&lt;br /&gt;  font-family: Georgia;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;p&gt;Next, some simple CSS tells the "tester" area to hide offscreen (and to turn off scrollbars for testing with desktop Safari).&lt;/p&gt;&lt;pre&gt;&lt;br /&gt;.tester {&lt;br /&gt;  position: absolute;&lt;br /&gt;  top: -1000px;&lt;br /&gt;  overflow: hidden;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;p&gt;And finally the meat:&lt;/p&gt;&lt;pre&gt;&lt;br /&gt;function textAreaSizer(obj) {&lt;br /&gt;  hiddenTextArea = document.createElement('textarea');&lt;br /&gt;  hiddenTextArea.rows = 20;&lt;br /&gt;  hiddenTextArea.className = 'tester';&lt;br /&gt;  obj.parentElement.appendChild(hiddenTextArea);&lt;br /&gt;  var last = '';&lt;br /&gt;&lt;br /&gt;  this.size = function(e) {&lt;br /&gt;    if (last != obj.value) {&lt;br /&gt;      last = obj.value;&lt;br /&gt;      hiddenTextArea.value = obj.value + "\n";&lt;br /&gt;      obj.style.height = hiddenTextArea.scrollHeight + 'px';&lt;br /&gt;    }&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  this.size();&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;p&gt;This closure accepts a textarea as input, and creates a sibling textarea using the "tester" class name. Note that I'm setting the hidden textarea's row size to 20, which means its scrollHeight will never fall below what's needed for 20 rows of text.&lt;/p&gt;&lt;p&gt;The web app in its current state is available &lt;a href="http://etchapps.appspot.com/notes.html"&gt;here&lt;/a&gt;. The source code doesn't try to obfuscate anything, but I still have some commenting and cleanup to do.&lt;/p&gt;&lt;p&gt;Enjoy!&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11885757-5439340503750743643?l=cautery.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://cautery.blogspot.com/feeds/5439340503750743643/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://cautery.blogspot.com/2011/04/designing-notes-iphone-web-app.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11885757/posts/default/5439340503750743643'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11885757/posts/default/5439340503750743643'/><link rel='alternate' type='text/html' href='http://cautery.blogspot.com/2011/04/designing-notes-iphone-web-app.html' title='Designing a Notes iPhone web app'/><author><name>Curtis Autery</name><uri>https://profiles.google.com/107677530285177731535</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh4.googleusercontent.com/--J3C_khtLqQ/AAAAAAAAAAI/AAAAAAAADT4/e2Qa5ncfAMk/s512-c/photo.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/-Yl-lncVUyHY/TbGib0gqYWI/AAAAAAAADRA/scnJ9-vENl8/s72-c/notesIconScreenshot.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11885757.post-3323308881025409864</id><published>2011-04-13T20:48:00.004-04:00</published><updated>2011-04-13T20:52:38.223-04:00</updated><title type='text'>Emulating iPhone rotating buttons with HTML5 canvases</title><content type='html'>This is a quick proof of concept tested with Chrome and Safari (which might also work in Firefox... testers?). I'm using timeouts and HTML5 canvas operations to spin buttons, and some DOM tomfoolery to add and remove rows to the document's body. The end result is an approximation of some iPhone settings screens that make you unlock delete buttons before you can remove items.&lt;br /&gt;&lt;br /&gt;Enjoy!&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;style&gt;.minus {  float: left;  margin: 4px;  display: none;}.btn {  float: right;  margin: 10px;  background: red;  line-height: 40px;  color: white;  height: 30px;  font-weight: bold;  text-align: center;  color: white;  line-height: 28px;  -webkit-border-radius: 7px;  padding-left: 6px;  padding-right: 6px;  display: none;  text-indent: 0;}.row {  height: 50px;  line-height: 50px;  width: 100%;  border: 1px solid black;  margin-bottom: 5px;  text-indent: 10px;}&lt;/style&gt;&lt;script type="text/javascript"&gt;var cr = Math.PI*2; // Circlevar deg = Math.PI/180; // One degreevar count = 0;function drawMinus(obj) {  // Get canvas context, and clear previous image  var ctx = obj.getContext('2d');  ctx.clearRect(0, 0, 40, 40);  // Red circle  ctx.fillStyle = "red";  ctx.beginPath();  ctx.arc(20, 20, 20, 0, cr, true);  ctx.fill();  // White minus sign, rotated to obj.angle degrees  ctx.save();  ctx.translate(20, 20);  ctx.rotate(obj.angle * deg);  ctx.fillStyle = "white";  ctx.fillRect(-11, -3, 22, 6);  ctx.restore();}function drawPlus(obj) {  // Get canvas context  var ctx = obj.getContext('2d');  // Green circle  ctx.fillStyle = "green";  ctx.beginPath();  ctx.arc(20, 20, 20, 0, cr, true);  ctx.fill();  // White plus sign  ctx.fillStyle = "white";  ctx.fillRect(9, 17, 22, 6);  ctx.fillRect(17, 9, 6, 22);}function makeDiv(className, cellText) {  var cell = document.createElement('div');  cell.className = className;  cell.innerText = cellText;  return cell;}function addButton() {  var container = document.getElementById('btnContainer');  var rowDiv = makeDiv('row', 'Row ' + (++count));  container.appendChild(rowDiv);  var deleteBtn = makeDiv('btn', 'Delete');  rowDiv.appendChild(deleteBtn);  deleteBtn.onclick = function() { container.removeChild(rowDiv); }  var cnv = document.createElement('canvas');  cnv.className = 'minus';  cnv.width = 40;  cnv.height = 40;  cnv.onclick = function() { spinLeft(cnv) };  cnv.angle = 0;  cnv.callbackRight = function() {    deleteBtn.style.display = null;    cnv.onclick = function() { spinLeft(cnv) };  }  cnv.callbackLeft = function() {    deleteBtn.style.display = 'block';    cnv.onclick = function() { spinRight(cnv) };  }  rowDiv.appendChild(cnv);  drawMinus(cnv);}function spinLeft(obj) { spin(obj, -15, -90); }function spinRight(obj) { spin(obj, 15, 0); }function spin(obj, step, goal) {  if (obj.onclick) obj.onclick = null;  if (step == 0) return;  if ((step &lt; 0 &amp;&amp; obj.angle &lt;= goal) || (step &gt; 0 &amp;&amp; obj.angle &gt;= goal)) {    obj.angle = goal;    if (step &lt; 0) obj.callbackLeft();    else obj.callbackRight();    return;  }  obj.angle += step;  drawMinus(obj);  setTimeout(function() {spin(obj, step, goal)}, 30);}function init() {  var styleSheet = document.styleSheets[document.styleSheets.length - 1];  var container = document.getElementById('btnContainer');  var cnv = document.createElement('canvas');  cnv.width = 40;  cnv.height = 40;  cnv.onclick = function() { spinLeft(cnv) };  cnv.angle = 0;  cnv.callbackRight = function() {    styleSheet.cssRules[0].style.display = 'none';    var elements = document.getElementsByClassName('minus');    for (var n = 0; n &lt; elements.length; n++) spinRight(elements[n]);    cnv.onclick = function() { spinLeft(cnv) };  }  cnv.callbackLeft = function() {    styleSheet.cssRules[0].style.display = 'block';    cnv.onclick = function() { spinRight(cnv) };  }  drawMinus(cnv);  container.appendChild(cnv);  var cnvPlus = document.createElement('canvas');  cnvPlus.width = 40;  cnvPlus.height = 40;  drawPlus(cnvPlus);  cnvPlus.style.float = 'right';  cnvPlus.onclick = addButton;  container.appendChild(cnvPlus);  addButton();}document.body.onload = init;&lt;/script&gt;&lt;div id=btnContainer&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11885757-3323308881025409864?l=cautery.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://cautery.blogspot.com/feeds/3323308881025409864/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://cautery.blogspot.com/2011/04/emulating-iphone-rotating-buttons-with_13.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11885757/posts/default/3323308881025409864'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11885757/posts/default/3323308881025409864'/><link rel='alternate' type='text/html' href='http://cautery.blogspot.com/2011/04/emulating-iphone-rotating-buttons-with_13.html' title='Emulating iPhone rotating buttons with HTML5 canvases'/><author><name>Curtis Autery</name><uri>https://profiles.google.com/107677530285177731535</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh4.googleusercontent.com/--J3C_khtLqQ/AAAAAAAAAAI/AAAAAAAADT4/e2Qa5ncfAMk/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11885757.post-866897149267894445</id><published>2011-04-08T08:53:00.000-04:00</published><updated>2011-04-08T08:53:08.013-04:00</updated><title type='text'>HTML5 Web Databases in Chrome, round 1</title><content type='html'>New fact of life: To keep up with modern web programming, you need to get your head around asynchronous callbacks. To illustrate, here is a quick script I wrote to experiment with Chrome's JavaScript calls into it's SQLite implementation:&lt;br /&gt;&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;&lt;pre&gt;var output = null;&lt;br /&gt;&lt;br /&gt;function onSuccess(transaction, result) {&lt;br /&gt;  output = result.rows.item(0).sql;&lt;br /&gt;  console.log('onSuccess completed');&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;function getDB(dbName) {&lt;br /&gt;  var fm = 5*1024*1024; // Five Megs&lt;br /&gt;  var db = openDatabase(dbName, '1.0', dbName, fm);&lt;br /&gt;  console.log('Database opened');&lt;br /&gt;&lt;br /&gt;  this.runSql = function(sql) {&lt;br /&gt;    db.readTransaction(function(t){&lt;br /&gt;      t.executeSql(sql, [], onSuccess)&lt;br /&gt;    });&lt;br /&gt;    console.log('runSql completed');&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  return this;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;var joe = getDB('db');&lt;br /&gt;joe.runSql("select * from sqlite_master");&lt;br /&gt;console.log('output: ' + output);&lt;br /&gt;console.log('End of script');&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Until it dawned on me that the SQL execution was asynchronous, I spent about 30 minutes chasing my tail trying to find a variable scope problem. In the console debugger, everything seemed to be set right when I manually entered commands one at a time, but the script didn't seem to be setting them when it ran. Didn't &lt;b&gt;seem&lt;/b&gt; to be, but it just wasn't happening when I thought.&lt;br /&gt;&lt;br /&gt;The above script was a re-write of what I was doing initially, with loggers at each step to show when the "output" variable gets set. Unsurprisingly, it happens on its own good time, not when the "procedural" part of the script reaches the end.&lt;br /&gt;&lt;br /&gt;Here's a screenshot of the console output:&lt;br /&gt;&lt;br /&gt;&lt;img src="https://lh3.googleusercontent.com/_YH1HTjXGzNU/TZ8EPEsX9rI/AAAAAAAADQk/vtC366bob6o/s640/DBcallsAreAsync.PNG" /&gt;&lt;br /&gt;&lt;br /&gt;The value of "output" is still null when the script reaches its last line. Manually checking the value after onSuccess completes shows that it does get set correctly, just later. So, there you have it. Writing that iPhone app or new web game? Start thinking async.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11885757-866897149267894445?l=cautery.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://cautery.blogspot.com/feeds/866897149267894445/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://cautery.blogspot.com/2011/04/html5-web-databases-in-chrome-round-1.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11885757/posts/default/866897149267894445'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11885757/posts/default/866897149267894445'/><link rel='alternate' type='text/html' href='http://cautery.blogspot.com/2011/04/html5-web-databases-in-chrome-round-1.html' title='HTML5 Web Databases in Chrome, round 1'/><author><name>Curtis Autery</name><uri>https://profiles.google.com/107677530285177731535</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh4.googleusercontent.com/--J3C_khtLqQ/AAAAAAAAAAI/AAAAAAAADT4/e2Qa5ncfAMk/s512-c/photo.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='https://lh3.googleusercontent.com/_YH1HTjXGzNU/TZ8EPEsX9rI/AAAAAAAADQk/vtC366bob6o/s72-c/DBcallsAreAsync.PNG' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11885757.post-7953709664290972226</id><published>2011-04-06T19:35:00.005-04:00</published><updated>2011-04-06T20:21:14.462-04:00</updated><title type='text'>Rage against the Pitocin-Cesarean Complex</title><content type='html'>(and how I wrote my first iPhone web app)&lt;blockquote&gt;"So I got me a pen and a paper and I made up my own little sign.I said thank you Lord for thinking about me, I'm alive and doing fine." - Five Man Electrical Band&lt;/blockquote&gt;&lt;p&gt;Before rambling about my latest tinkerings, I have a small announcement: My wife and I decided to have a baby. She asked me not to make a big deal about it online, not post a broadcast "guess what?!?!" message on Facebook, and in the spirit of that I won't make flowery pronouncements here about the joys of fatherhood, or starting over at 40, or any of the hundreds of other things I would otherwise go on at length about. But there it is: I'm in love with a woman I think the world of, and at the beginning of January, with our third wedding anniversary near us and neither of us having one foot out the door or buyer's remorse, we decided to stop using birth control and try to have a baby.&lt;/p&gt;&lt;p&gt;By the end of the month, Liberty was pregnant.&lt;a name='more'&gt;&lt;/a&gt;When she was pregnant with Scout, Liberty wanted as natural and non-medicalized birth as possible, preferably a home birth with a midwife to assist. In Ohio, home birth and birthing centers are in a grey area legally. Instead of outright declaring that birth is only allowed in hospitals, the cost of liability insurance for midwifery prices many centers out of existence, and not all medical insurance plans cover them. Liberty unfortunately could not find a local center who took her insurance, but found one in Cincinnati that did. During a 37th week appointment with them, Liberty's blood pressure was elevated, so her midwife recommended going to a hospital for a urine test. At the hospital, the doctors were quick to diagnose preeclampsia, and demanded that she stay and be induced immediately. Needless to say, this wasn't the birth plan she had in mind.&lt;/p&gt;&lt;p&gt;The hospital tried to enforce the standard medical interventions designed to get the doctor to the golf course by tee time: Pitocin, epidural, Cesarean. Her midwife (bless her heart) went to bat for Liberty, telling them to back the hell off, insisting on a private room with a bathtub, that she be allowed to get up and move around, have access to food to keep her strength up, and helped Liberty maintain as much control over the process as possible. Scout's dad was caught between dueling ideologies and unsure of whose side to take. As much as I like him, I don't think he had the life experience needed to cope with a hospital trying to force its agenda on you through scare tactics. He tended to side with the doctors more often than not, but nothing in his experience told him to have anything but faith in what a doctor recommended. Despite that, Liberty's force of will and the strong advocacy of the midwife kept the unnecessary interventions to a minimum.&lt;/p&gt;&lt;p&gt;To be fair to Scout's dad, when Stacey was born, I was of a similar mindset, and Stacey's mom and I thought the hospital was just where you were supposed to go for labor. It wasn't until years later that it dawned on me that doctors are normal people with their own agenda, and a healthy skepticism was needed. I also knew that people still gave birth before there were hospitals, and people were trained to assist the mother before the field of medical obstetrics was invented. It wasn't until recently that it all clicked together for me: In the 60 years that hospital birth in developed countries has been the norm, we've progressed only marginally from twilight sleep and leather straps. The medical community continues to lack empathy and support for mothers in labor, and until this changes, hospital births should be avoided where possible. The field of obstetrics has done too much to disempower and dehumanize women in what is supposed to be an important, memorable, beautiful event. Women should be revered while they're creating life; doing otherwise is God-damned unconscionable.&lt;/p&gt;&lt;p&gt;Back to the story: Because Liberty was induced before her body was ready, her body balked, and she endured a painful 3 day labor. Despite the long labor, the constant onslaught of doctors saying, as if scolding a small child for not cleaning her room, "you know, it's going to be time for a C-section if you don't progress soon," and despite the physical pain of prolonged use of Pitocin, Scout was born vaginally, without need of scalpel or forceps, and from what I've divined in the 3 and a half years I've known her, is both healthy and intelligent. Everything ended well with Scout's birth, but needless to say that isn't the type of birth we want this time.&lt;/p&gt;&lt;p&gt;We found a good midwife center here in Columbus, &lt;a href="http://www.choicemidwives.org/"&gt;CHOICE&lt;/a&gt;, to see during the pregnancy, and we intend to have the baby on &lt;a href="http://www.thefarmmidwives.org/"&gt;The Farm&lt;/a&gt;, a commune that managed to survive after the 70s when the popular hippie movement died down, and home to the mother of modern midwifery, Ina May Gaskin. Ina May authored &lt;a href="http://www.bookpubco.com/products/spiritual-midwifery"&gt;"Spiritual Midwifery"&lt;/a&gt;, which I can't recommend highly enough to anyone planning on having a baby, and was interviewed in Ricki Lake's movie, &lt;a href="http://movies.netflix.com/WiMovie/The-Business-of-Being-Born/70075502"&gt;"The Business of Being Born"&lt;/a&gt;, a must-see for the expecting couple. She is also the namesake of the Gaskin Maneuver, a Guatemalan technique that she popularized that helps with shoulder dystocia - where an infant's shoulder gets stuck when they crown. (When we went down to visit The Farm recently, Liberty saw Ina May driving by, and was all excited. It was cute.)&lt;/p&gt;&lt;p&gt;After being frank about the previous birth to our CHOICE midwife, she said some things that we were happy to hear, namely that preeclampsia is more common in first-time mothers, and a high protein, high calcium diet is good insurance against a repeat occurrence. I found some articles and a study that supported both of those assertions, which was reassuring to me. Before meeting Liberty, I had no experience with what midwives were supposed to be (as is true of most Americans). Based on my experiences so far, I'm sold on them. They are competent professionals, more knowledgeable about childbirth than the surgeons who call themselves obstetricians. Midwives believe, like I do, that childbirth doesn't belong in a hospital with medical intervention, or with the mother on her back, scared, and taking orders.&lt;/p&gt;&lt;p&gt;So coupled with the protein/calcium diet advice, Liberty wanted to monitor her fluid and fiber intake as well, so she set out to look for an iPhone app that could help her track all that. Sadly there weren't any that had all four, or that were customizable. And this is where I come in, using my geek mojo to try to attack the problem.&lt;/p&gt;&lt;p&gt;About 6 months back, I signed up for Apple's developer program, giving them my gmail address, but never heard back. My main home workstations run XP and Linux, so to use XCode to write iPhone apps, I would need to confiscate Liberty's Macbook, giving me two reasons not to code a native app right off the bat. After some digging, I found that Safari supports the offline application cache and web storage specs, and iOS lets you bookmark a website to the "desktop" with a custom icon and splash screen, and provides controls to put Safari in a sort of kiosk mode. In other words, you can click on a button on the main screen, have it open a web app that can save data locally, and the result is basically indistinguishable from a native app. With this approach, I would be able to code for the iPhone with Windows or Linux, and not pay Apple for the right to do so, so that's the road I took.&lt;/p&gt;&lt;p class=section&gt;Getting started&lt;/p&gt;&lt;p&gt;At the beginning of this effort, I decided to code the UI from scratch using un-obfuscated JavaScript to build tables, "buttons" (&amp;lt;span&amp;gt; tags, actually), and onclick handlers. In an attempt to move things along quickly, I didn't fully separate out functionality using MVP, MVC, or other mainstream architectural patterns, but the UI and object functions are fairly split apart. To illustrate, here are functions to build a generic button (UI), and another to remove a food item from a day's eating history (object model).&lt;/p&gt;&lt;pre&gt;&lt;br /&gt;function makeButton(buttonText, callBack) {&lt;br /&gt;  var button = document.createElement('span');&lt;br /&gt;  button.className = 'button';&lt;br /&gt;  button.innerText = buttonText;&lt;br /&gt;  button.onclick = callBack;&lt;br /&gt;  return button;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;function removeItem(index, dayString) {&lt;br /&gt;  if (lentil[dayString] == null || lentil[dayString][index] == null) return;&lt;br /&gt;  lentil[dayString].splice(index, 1);&lt;br /&gt;  saveData();&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;p&gt;Whereas, here is sort of a kluge to return a table of a day's eating, that can't decide if it's the user interface, the object model, or a controller:&lt;/p&gt;&lt;pre&gt;&lt;br /&gt;function getDayTable(dayString) {&lt;br /&gt;  var outTable = initTable();&lt;br /&gt;  if (lentil[dayString] == null) lentil[dayString] = [];&lt;br /&gt;  var totals = [];&lt;br /&gt;  for (var n = 0; n &amp;lt; trackElements.length; n++) totals.push(0);&lt;br /&gt;&lt;br /&gt;  var counts = {};&lt;br /&gt;  for (var n in lentil[dayString]) {&lt;br /&gt;    var name = items[lentil[dayString][n]].name;&lt;br /&gt;    counts[name] = (counts[name] == null) ? 1 : counts[name] + 1;&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  var seen = {};&lt;br /&gt;  for (var n in lentil[dayString]) {&lt;br /&gt;    var itemNum = lentil[dayString][n];&lt;br /&gt;    var item = items[itemNum];&lt;br /&gt;    if (seen[item.name] == null) {&lt;br /&gt;      roundCollection(item.elements);&lt;br /&gt;      var row = collectionToCountsRow(item.name, item.elements, counts[item.name]);&lt;br /&gt;      var callback = new callbackRef('verifyRemove', itemNum, n, dayString);&lt;br /&gt;      row.onclick = callback.invoke;&lt;br /&gt;      outTable.appendChild(row);&lt;br /&gt;      incrementTotals(itemNum, totals, counts[item.name]);&lt;br /&gt;      seen[item.name] = true;&lt;br /&gt;    }&lt;br /&gt;  }&lt;br /&gt;  outTable.appendChild(totalsRow(totals));&lt;br /&gt;  return outTable;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;p&gt;There's still quite a bit of overlap, and as the code progresses I'll get better role separation. Round one, though, quick and dirty, weighing in at only 400 lines of JavaScript and giving my wife something usable while she's still pregnant, a more important concern than glass-tower programming theory.&lt;/p&gt;&lt;p class=section&gt;Lentil&lt;/p&gt;&lt;p&gt;You may have noticed references above to a hash named "lentil". When Liberty was early in her first trimester, she estimated the baby's size to be about that of a lentil. When she would have baby-related symptoms such as an upset stomach, she would say things like "Lentil is really doing a number on me today." So after she balked at my naming the first build of my program "Preggers", she recommended the name Lentil instead, which was more agreeable to both of us.&lt;/p&gt;&lt;p&gt;We shamelessly stole an image off the net for the home screen button:&lt;/p&gt;&lt;p&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/-r-rwz0egigk/TZz2DKKdfTI/AAAAAAAADOs/RTaB0fqoOjo/s1600/01a-iPhoneScreenshot.png" imageanchor="1" style=""&gt;&lt;img border="0" height="320" width="214" src="http://4.bp.blogspot.com/-r-rwz0egigk/TZz2DKKdfTI/AAAAAAAADOs/RTaB0fqoOjo/s320/01a-iPhoneScreenshot.png" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;/p&gt;&lt;p&gt;...but we have a wooden bowl and a bag of lentils at home with which to make a similar image of our own so we won't be dirty cheaters. Or get sued.&lt;/p&gt;&lt;p class=section&gt;Making a web page look like an app&lt;/p&gt;&lt;p&gt;There are a pair of HTML meta tags to enable fullscreen mode and darken the status bar (the top of the iPhone screen that shows your battery level, time, etc.) They are as follows:&lt;/p&gt;&lt;pre&gt;&lt;br /&gt;&amp;lt;meta name="apple-mobile-web-app-capable" content="yes" /&amp;gt;&lt;br /&gt;&amp;lt;meta name="apple-mobile-web-app-status-bar-style" content="black" /&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;p&gt;Another meta tag can change viewport settings, like disabling zooming, and setting the viewport's width and initial zoom levels:&lt;/p&gt;&lt;pre&gt;&lt;br /&gt;&amp;lt;meta name="viewport" content="user-scalable=no, width=device-width, initial-scale=1.0, maximum-scale=1.0"/&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;p&gt;The last iPhone-specific HTML-based hints I used are for setting the home screen icon and the splash-screen. The addresses are relative to the location of the page they are referenced from.&lt;/p&gt;&lt;pre&gt;&lt;br /&gt;&amp;lt;link rel="apple-touch-icon" href="images/icon.jpg"/&amp;gt;&lt;br /&gt;&amp;lt;link rel="apple-touch-startup-image" href="images/startup.png" /&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;p&gt;My splashscreen is a little cartoony right now; it's definitely something that will be professionalled-up when this comes out of beta. Here's the current image:&lt;/p&gt;&lt;p&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-TFtGGJux6h0/TZz2c9r1HBI/AAAAAAAADO0/i_w4DpidG1s/s1600/01b-startup.png" imageanchor="1" style=""&gt;&lt;img border="0" height="320" width="222" src="http://3.bp.blogspot.com/-TFtGGJux6h0/TZz2c9r1HBI/AAAAAAAADO0/i_w4DpidG1s/s320/01b-startup.png" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;/p&gt;&lt;p&gt;With those HTML tags on a page, you can visit it from Safari on the iPhone, bookmark it to your homescreen, and from then on it will open like a native app. More information about tags that will influence Safari on the iPhone are available &lt;a href="http://developer.apple.com/library/safari/#documentation/appleapplications/reference/SafariHTMLRef/Articles/MetaTags.html"&gt;here&lt;/a&gt; (as well as scores of other pages).&lt;/p&gt;&lt;p class=section&gt;Offline Web applications&lt;/p&gt;&lt;p&gt;The W3C's HTML5 working draft has a nice summary of how offline caching of web applications works, available &lt;a href="http://www.w3.org/TR/html5/offline.html"&gt;here&lt;/a&gt;. Briefly, the main HTML page references a manifest file. The manifest file lists what files to cache, and the browser caches them. The next time the page is opened, a single query to the web server is made to see if the manifest file has been altered. If it hasn't (or if the browser is offline), all the web resources are loaded from the cache instead of the network.&lt;/p&gt;&lt;p&gt;In other words, if I open a cached web application while the iPhone is in airplane mode, it will still open. Additionally, if the manifest file hasn't been updated, only a single HTTP GET is issued for the manifest file before the application opens, speeding up opening quite a bit.&lt;/p&gt;&lt;p&gt;The caveat is that the manifest file must be served with a content type of "text/cache-manifest" for browsers to turn on caching. In the near future, I imagine by default most web servers will serve .manifest files with this content type, but currently some twiddling is needed - at least, that's true on my current web server of choice, Google's App Engine.&lt;/p&gt;&lt;p&gt;I use Eclipse/Java for deploying App Engine projects, and changing the content type served for a given file extension is accomplished with a few lines in the project's web.xml file:&lt;/p&gt;&lt;pre&gt;&lt;br /&gt;&amp;lt;mime-mapping&amp;gt;    &lt;br /&gt;  &amp;lt;extension&amp;gt;manifest&amp;lt;/extension&amp;gt;        &lt;br /&gt;  &amp;lt;mime-type&amp;gt;text/cache-manifest&amp;lt;/mime-type&amp;gt;        &lt;br /&gt;&amp;lt;/mime-mapping&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;p&gt;To verify this was working as expected, I deployed a project, telnetted to the web server on port 80, and did a manual GET for the file:&lt;/p&gt;&lt;pre&gt;&lt;br /&gt;GET /lentil.manifest HTTP/1.1&lt;br /&gt;Host: lentiltracker.appspot.com&lt;br /&gt;&lt;br /&gt;HTTP/1.1 200 OK&lt;br /&gt;ETag: "kRe4nQ"&lt;br /&gt;Date: Sat, 02 Apr 2011 08:47:45 GMT&lt;br /&gt;Expires: Sat, 02 Apr 2011 08:57:45 GMT&lt;br /&gt;Cache-Control: public, max-age=600&lt;br /&gt;&lt;b&gt;Content-Type: text/cache-manifest&lt;/b&gt;&lt;br /&gt;Server: Google Frontend&lt;br /&gt;Transfer-Encoding: chunked&lt;br /&gt;&lt;br /&gt;a3&lt;br /&gt;CACHE MANIFEST&lt;br /&gt;&lt;br /&gt;# Beta 9.&lt;br /&gt;&lt;br /&gt;# js files&lt;br /&gt;json2.js&lt;br /&gt;lentil.js&lt;br /&gt;...rest of file&lt;br /&gt;0&lt;br /&gt;&lt;/pre&gt;&lt;p&gt;As you can see, the content type came through correctly. Rather than the browser downloading the entire file each time and comparing it with a cached copy, the HTTP entity tag reference(&lt;a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.19"&gt;ETag&lt;/a&gt;) is used to determine if the web server thinks the file has changed. This is accomplished by including an&lt;a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.26"&gt;"If-None-Match"&lt;/a&gt; field in the GET request, specifying the last ETag value. If the browser gets back an HTTP 304 response, it knows there's no need to re-download the entire file. Here's how that looks over telnet:&lt;/p&gt;&lt;pre&gt;&lt;br /&gt;GET /lentil.manifest HTTP/1.1&lt;br /&gt;Host: lentiltracker.appspot.com&lt;br /&gt;If-None-Match: "kRe4nQ"&lt;br /&gt;&lt;br /&gt;HTTP/1.1 304 Not Modified&lt;br /&gt;ETag: "kRe4nQ"&lt;br /&gt;Date: Sat, 02 Apr 2011 08:53:31 GMT&lt;br /&gt;Server: Google Frontend&lt;br /&gt;&lt;/pre&gt;&lt;p&gt;My project has a number of resource files with it: The main HTML page, a CSS file, JavaScript file, the icon and splashscreen images, and of course the manifest file. My App Engine resource usage will look something like this for requests that come after all of those files have been cached:&lt;/p&gt;&lt;p&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/-ZRfMk6cRs6s/TZz2rEK635I/AAAAAAAADO8/dhMLSdkGzfM/s1600/02-recentRequests.png" imageanchor="1" style=""&gt;&lt;img border="0" height="83" width="320" src="http://1.bp.blogspot.com/-ZRfMk6cRs6s/TZz2rEK635I/AAAAAAAADO8/dhMLSdkGzfM/s320/02-recentRequests.png" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;/p&gt;&lt;p&gt;Ideally, just the manifest file, and most of those should be HTTP 304s. What I've found in practice, though, is that App Engine gives the occasional HTTP 200 for a file that hasn't changed, which leads me to believe they don't always store the ETag field the same way on all mirrored servers, or possibly each App instance regenerates new values. Consequently, Safari busies itself with comparing the file contents, and sees that all is well so doesn't re-download the CSS, JavaScript or main page files. In some of these cases, the iPhone's desktop subsystem will re-download the icon and splashscreen files, sending a user agent containing CFNetwork and Darwin, contrasted with Safari's AppleWebKit. A typical set of log entries looks like this:&lt;/p&gt;&lt;p&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/-zGPi4FEKW0M/TZz2y9MIoEI/AAAAAAAADPE/0t0G-EGKZ2E/s1600/03-serverLog.png" imageanchor="1" style=""&gt;&lt;img border="0" height="130" width="320" src="http://2.bp.blogspot.com/-zGPi4FEKW0M/TZz2y9MIoEI/AAAAAAAADPE/0t0G-EGKZ2E/s320/03-serverLog.png" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;/p&gt;&lt;p&gt;Even considering that the 304s aren't consistent, this is still a lot less traffic than having each file be downloaded fresh each time the app runs. The web traffic benefit is good reason to use application caching of static content (or code) where possible, even if you aren't trying to make a web page look like a native iPhone app.&lt;/p&gt;&lt;p&gt;Since Safari on the iPhone and Google Chrome both use the WebKit engine, I did most of my testing for this project with Chrome on Windows for the basic reason that its debugging tools are fantastic. For example, from the chrome://net-internals/ URL, you can capture and view raw network bytes, as well as see when Chrome is grabbing a resource from the application cache instead of the network. For example, here is how Chrome responded to the order to load my app's CSS file:&lt;/p&gt;&lt;pre&gt;&lt;br /&gt;Start Time: Sat Apr 02 2011 04:33:06 GMT-0400 (Eastern Daylight Time)&lt;br /&gt;&lt;br /&gt;t=1301733186906 [st= 0] +REQUEST_ALIVE             [dt=69]&lt;br /&gt;t=1301733186906 [st= 0]    +URL_REQUEST_START_JOB  [dt=29]&lt;br /&gt;                            --&amp;gt; load_flags = 65536 (VERIFY_EV_CERT)&lt;br /&gt;                            --&amp;gt; method = "GET"   &lt;br /&gt;                            --&amp;gt; priority = 1     &lt;br /&gt;                            --&amp;gt; url = "http://lentiltracker.appspot.com/lentil.css"&lt;br /&gt;t=1301733186916 [st=10]        APPCACHE_DELIVERING_CACHED_RESPONSE  &lt;br /&gt;t=1301733186935 [st=29]    -URL_REQUEST_START_JOB  &lt;br /&gt;t=1301733186975 [st=69] -REQUEST_ALIVE &lt;br /&gt;&lt;/pre&gt;&lt;p&gt;In fact, the net-internals page is where I first noticed the business with ETags and HTTP 304 responses:&lt;/p&gt;&lt;p&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/-xDpw2rpheSI/TZz3TSMwoWI/AAAAAAAADPM/NBPhzBSQVZM/s1600/10-netInternals.PNG" imageanchor="1" style=""&gt;&lt;img border="0" height="311" width="320" src="http://4.bp.blogspot.com/-xDpw2rpheSI/TZz3TSMwoWI/AAAAAAAADPM/NBPhzBSQVZM/s320/10-netInternals.PNG" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;/p&gt;&lt;p&gt;If you want to see all the stored manifest files, or delete individual application caches, you can accomplish that from chrome://appcache-internals/, as shown in this screenshot:&lt;/p&gt;&lt;p&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/-A8NN3yg2wQs/TZz3p4n2TkI/AAAAAAAADPU/CiybqfRinko/s1600/09-appcacheInternals.png" imageanchor="1" style=""&gt;&lt;img border="0" height="320" width="300" src="http://4.bp.blogspot.com/-A8NN3yg2wQs/TZz3p4n2TkI/AAAAAAAADPU/CiybqfRinko/s320/09-appcacheInternals.png" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;/p&gt;&lt;p class=section&gt;Storing data locally&lt;/p&gt;&lt;p&gt;In the past, having a browser store data locally could only be accomplished with HTTP cookies, the much-maligned bastard stepchild of the Internet. There are two major drawbacks to using them, namely size limits (4k per cookie, 20 cookies per domain), and the fact that they are sent by the client with every request. The original Netscape recommendation for cookies indicated the idea was for them to manage "state". The server would be notified what state the browser thought it was in, and the server could make decisions based on that.&lt;/p&gt;&lt;p&gt;A newer recommendation for &lt;a href="http://www.w3.org/TR/webstorage/"&gt;web storage&lt;/a&gt; is being hashed out for the HTML5 standard, and Chrome and Safari (and I assume other browsers) already have it implemented, complete with a handy JavaScript accessor: localStorage.[name] = [string] for storing simple strings. There is also a web database recommendation that allows local DB objects that can be queried with SQL, but I chose the simple localStorage method so I could get something functional quickly, and used JSON to turn my JavaScript objects into strings.&lt;/p&gt;&lt;p&gt;I ended up with a pair of simple functions that manage turning an eating history object and a food types object into localStorage strings, and back again:&lt;/p&gt;&lt;pre&gt;&lt;br /&gt;var lentil = {}; // History hash&lt;br /&gt;var items = [];  // Array of food types&lt;br /&gt;&lt;br /&gt;function init() {&lt;br /&gt;  if (localStorage == null) {&lt;br /&gt;    alert("Local storage features won't work in this browser.");&lt;br /&gt;  } else {&lt;br /&gt;    if (localStorage.lentilHistory) lentil = JSON.parse(localStorage.lentilHistory);&lt;br /&gt;    if (localStorage.lentilItems) items = JSON.parse(localStorage.lentilItems);&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;function saveData() {&lt;br /&gt;  localStorage.lentilHistory = JSON.stringify(lentil);&lt;br /&gt;  localStorage.lentilItems = JSON.stringify(items);&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;p&gt;Chrome's developer tools can be used to see the raw contents of the local storage DOM tree, as shown here:&lt;/p&gt;&lt;p&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/-vSihvR_x-1U/TZz3xejaOxI/AAAAAAAADPc/9iG7BcCszIU/s1600/11-localStorage.png" imageanchor="1" style=""&gt;&lt;img border="0" height="135" width="320" src="http://2.bp.blogspot.com/-vSihvR_x-1U/TZz3xejaOxI/AAAAAAAADPc/9iG7BcCszIU/s320/11-localStorage.png" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;/p&gt;&lt;p&gt;Now that I have my feet under me with web apps, I can either databasify this, or at least split foods and days into unique objects so that the entire history can't be blown away with one bad write. How to manage doing that and save all the data Liberty already has entered will take some hemming and hawing, but shouldn't be too much of a puzzler.&lt;/p&gt;&lt;p&gt;The rest is all JavaScript to handle events and turn arrays of food items into tables. Here are a few screenshots of how the pages look now, but it needs a little usability tweaking, and a lot of prettying up.&lt;/p&gt;&lt;p&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/-VLRWb9lmTQw/TZz4IhnFIdI/AAAAAAAADPk/rTVdg56u-Zs/s1600/04-Calendar.png" imageanchor="1" style=""&gt;&lt;img border="0" height="320" width="260" src="http://2.bp.blogspot.com/-VLRWb9lmTQw/TZz4IhnFIdI/AAAAAAAADPk/rTVdg56u-Zs/s320/04-Calendar.png" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/-M-mINdV85mo/TZz4I83WEkI/AAAAAAAADPs/vETZcNJ_q-g/s1600/05-newFood.png" imageanchor="1" style=""&gt;&lt;img border="0" height="273" width="320" src="http://4.bp.blogspot.com/-M-mINdV85mo/TZz4I83WEkI/AAAAAAAADPs/vETZcNJ_q-g/s320/05-newFood.png" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/-HqMUmnhBBA8/TZz4JbR5AKI/AAAAAAAADP0/jTjqGCkLEbE/s1600/07-showDay.png" imageanchor="1" style=""&gt;&lt;img border="0" height="257" width="320" src="http://1.bp.blogspot.com/-HqMUmnhBBA8/TZz4JbR5AKI/AAAAAAAADP0/jTjqGCkLEbE/s320/07-showDay.png" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/-EtI3ov8wduM/TZz4J93s2OI/AAAAAAAADP8/RHtNBXc88OA/s1600/08-summary.png" imageanchor="1" style=""&gt;&lt;img border="0" height="320" width="258" src="http://2.bp.blogspot.com/-EtI3ov8wduM/TZz4J93s2OI/AAAAAAAADP8/RHtNBXc88OA/s320/08-summary.png" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;/p&gt;More tweaks of the app will come, and in all likelihood I'll leave it up on the &lt;a href="http://lentiltracker.appspot.com/"&gt;App Engine page&lt;/a&gt; as a free tool. The end result that I care about, though, is Liberty's use of it. She is tracking her calcium and protein, and can see at a glance if she's on the road to avoiding a second hospital birth. As a husband who cares about this sort of thing, I'm doing everything in my power to support a natural birth. I'm just glad in this case there is enough overlap between what I'm good at and what Liberty needs so that I can contribute.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11885757-7953709664290972226?l=cautery.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://cautery.blogspot.com/feeds/7953709664290972226/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://cautery.blogspot.com/2011/04/rage-against-pitocin-cesarean-complex.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11885757/posts/default/7953709664290972226'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11885757/posts/default/7953709664290972226'/><link rel='alternate' type='text/html' href='http://cautery.blogspot.com/2011/04/rage-against-pitocin-cesarean-complex.html' title='Rage against the Pitocin-Cesarean Complex'/><author><name>Curtis Autery</name><uri>https://profiles.google.com/107677530285177731535</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh4.googleusercontent.com/--J3C_khtLqQ/AAAAAAAAAAI/AAAAAAAADT4/e2Qa5ncfAMk/s512-c/photo.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/-r-rwz0egigk/TZz2DKKdfTI/AAAAAAAADOs/RTaB0fqoOjo/s72-c/01a-iPhoneScreenshot.png' height='72' width='72'/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11885757.post-5396732055206039059</id><published>2011-03-05T11:46:00.001-05:00</published><updated>2011-03-05T11:47:10.974-05:00</updated><title type='text'>Success through embracing mistakes</title><content type='html'>&lt;blockquote&gt;&lt;i&gt;"The dogmas of the quiet past, are inadequate to the stormy present. The occasion is piled high with difficulty, and we must rise -- with the occasion. As our case is new, so we must think anew, and act anew. We must disenthrall ourselves, and then we shall save our country."&lt;/i&gt; - Abraham Lincoln&lt;/blockquote&gt;"I hate math, dad" my daughter said to me a few days ago. It wasn't really a shock to hear it, rather it was the final nail in the coffin, which I'd seen coming for the last few years. The middle school years, sitting in rows, staring at the backs of her peers' heads, listening to lectures, seeing story problems which were so fanciful as to be ridiculous. So Bill's dad's age is 5 less than 3 times Bill's age, and the sum of their ages is 49? Really? Does that sort of thing come up in life a lot?&lt;br /&gt;&lt;br /&gt;&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;As a toddler, we played with shapes and numbers for fun. I showed her how fractions work at 4 years old, by drawing on my whiteboard at the office. I drew a circle and drew lines through it, then shaded parts in and wrote down the fraction it represented. With some experimentation, she saw how shading two quarters of a circle took up the same space as one half. She "got it", and was thrilled to figure out fractions. Innoculated against the idea that math is hard, she spent all of her elementary school years scoring off the charts in math, and was offered a spot in an advanced math/science program in the 4th and 5th grades, going to a different school once a week to work solely on math and science topics.&lt;br /&gt;&lt;br /&gt;And then there was middle school. Now in high school, with the middle school craziness coupled with years of peer pressure to reject appearing smart in order to be more popular, her standardized test scores slipped from "phenomenal" to merely "advanced". The coup de grâce happened this year, when she told me flatly how she feels about math. Another creative spirit successfully crushed by an education system designed in the 19th century to meet the needs of industrialism.&lt;br /&gt;&lt;br /&gt;Something similar happened to me, back in the day. One of the reasons that I didn't go to college is that traditional education doesn't agree with me. In school I learned many strategies for test-taking that did not involve learning the subject matter of the class. I felt stifled by the needless repetition of lessons. I dislike the overall layout of textbooks, and the transparent committee thinking that goes into them.&lt;br /&gt;&lt;br /&gt;What I disliked the most, though, was the idea that mistakes were bad, and not a tool for learning. A test would have, say, 20 questions on a chapter's topic, and you would have 20 chances to make a mistake that would cost you points. In classes where I "got it" to the point of being bored and wanting to move on, the repetition would get me. By question 5 I'd be hurrying through the uninteresting test and making speed-based errors rather than mistakes based on not knowing the material. In classes where I didn't "get it", the opposite behavior of going slower and using test-taking strategies artificially inflated my test scores, and made it seem that I knew material that I didn't.&lt;br /&gt;&lt;br /&gt;Somehow I managed to come out of school with a love of numbers, and of science, history, and books, but all of that was at risk of being crushed by traditional schooling. For me it was a close shave with rejecting all academics, as I was more focused on passing tests and getting the hell away from school than I was in embracing what was being taught. It was a lot of effort to find the tiny nuggets of "interesting" buried in the rubble of textbooks and lesson plans, but I eventually found them. My high school helped when I was on the brink of throwing it all away. I went to the &lt;a href="http://linworth.org/program.htm"&gt;Linworth Alternative Program&lt;/a&gt;, whose teachers were much easier to cope with. The AP was a much better approach to education than I had seen in the seven schools I had been to previously, but even they were subjected to the tyranny of test = grade.&lt;br /&gt;&lt;br /&gt;The running theme that school threw at both my daughter and myself is this: Pass the test. The more mistakes you make, the worse you "do" in the class, regardless of what skills you learn. Measuring knowledge or understanding is not happening, only test-taking ability is being measured. To prepare for a test, one crams, regurgitates, and forgets. Next year, the teacher has to re-explain the basics supposedly learned the previous year before starting any new material.&lt;br /&gt;&lt;br /&gt;Outside of schools, making mistakes is a tool that increases your understanding of the world around you. When learning a new skill, you will be very error-prone at first as your brain and body try to cope with something they have never done before. When you first learn to play the piano, you can't make it through a piece error-free to save your life. Eventually the smaller skills that make a musician get mastered: sight-reading music, hand separation, hearing the piece in your head, finger strength and speed, passion.&lt;br /&gt;&lt;br /&gt;Any junior level software developer tasked with solving a new problem will tell you this story: "I took the problem statement and started coding for it. Once I figured out what I was doing wrong, I better understood the nature of the problem. Then I started over and wrote a better program." A more advanced developer will tell you the real value of tests is in failing them. In fact, the Test Driven Development model is exactly that: Write a test for the requirement before you write any code, a test that is guaranteed to fail out of the gate. Run the test and watch it fail. (If it doesn't fail, your test is flawed). Code for the new test, then run all the tests again, until nothing fails. (There's more to it than that outside the scope of this screed, and if you're interested you can get more info &lt;a href="http://books.google.com/books?id=gFgnde_vwMAC&amp;amp;lpg=PP1&amp;amp;dq=kent%20beck&amp;amp;pg=PP1#v=onepage&amp;amp;q&amp;amp;f=false"&gt;here&lt;/a&gt;.)&lt;br /&gt;&lt;br /&gt;In my job, tests are for tuning functionality, and if you don't fail a bunch of tests in the process of writing your program, you're either a damned genius or you're writing something simplistic and boring. In school, tests immediately serve to bring down your grade in the class, so the students must concentrate not on learning skills, but on passing tests. Will this be on the test? What's the answer the teacher wants?&lt;br /&gt;&lt;br /&gt;This is all ass-backwards on its face, and judging by my daughter Stacey's experiences 20 years after my own, schools are still being run the same way. There is advocacy for change, by the likes of &lt;a href="http://www.ted.com/talks/lang/eng/dan_meyer_math_curriculum_makeover.html"&gt;Dan Meyer&lt;/a&gt; and &lt;a href="http://www.ted.com/talks/view/id/66"&gt;Ken Robinson&lt;/a&gt;, but change is slow in coming.&lt;br /&gt;&lt;br /&gt;My solution? Grade all classes the way arts classes are graded, with the three Ps: Participation, passion, and projects. In drama class, a play can demonstrate the skills of memorization, timing, getting into character. In drawing classes, projects can demonstrate understanding of perspective, observation, different mediums.&lt;br /&gt;&lt;br /&gt;In math classes, is it important that students can calculate by rote dozens of times in a row? No, unless they are yearning for assembly line work. Instead, how about projects demonstrating the desired skills? Look for a &lt;b&gt;real life&lt;/b&gt; problem that can be measured and have math principles applied to it. Mom and Dad each have cars with different MPGs, and they work different distances from home. How can they save gas? Dad's credit cards are maxed out, and he has $40 extra dollars a month dedicated to paying them off. Each card has a different balance and interest rate. In what order should he apply the $40 to pay them off the quickest?&lt;br /&gt;&lt;br /&gt;Math class would be the easiest to reform, since I think everyone would agree it's the most broken. Other classes would be harder (by me, anyway) to modernize, but I believe similar project-based approaches are possible for them as well. I also believe schools should no longer be aligned with the needs of the business world. They should export thinkers, not workers. Businesses can take care of their own needs, and will find other ways to make drones when they no longer have the explicit consent of public education.&lt;br /&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11885757-5396732055206039059?l=cautery.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://cautery.blogspot.com/feeds/5396732055206039059/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://cautery.blogspot.com/2011/03/success-through-embracing-mistakes.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11885757/posts/default/5396732055206039059'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11885757/posts/default/5396732055206039059'/><link rel='alternate' type='text/html' href='http://cautery.blogspot.com/2011/03/success-through-embracing-mistakes.html' title='Success through embracing mistakes'/><author><name>Curtis Autery</name><uri>https://profiles.google.com/107677530285177731535</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh4.googleusercontent.com/--J3C_khtLqQ/AAAAAAAAAAI/AAAAAAAADT4/e2Qa5ncfAMk/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11885757.post-2797651215583651036</id><published>2011-01-21T07:42:00.001-05:00</published><updated>2011-01-21T20:23:53.435-05:00</updated><title type='text'>4,046</title><content type='html'>I've disabled this blog's Google Analytics feed. The feed assures me that there were 4,046 visits here over the past year, most of which occurred after a swell of visits in August '10 when I started posting about various coding experiments I was working on. From that time forward, there were over 100 visits per week to the site, most of which turned out not to be loyal fans, but rather one time visits referred here by search engines.&lt;br /&gt;&lt;br /&gt;&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/_YH1HTjXGzNU/TTl-5R4r7sI/AAAAAAAADLQ/NhnaHCiTA1s/s1600/visitors1.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="127" src="http://4.bp.blogspot.com/_YH1HTjXGzNU/TTl-5R4r7sI/AAAAAAAADLQ/NhnaHCiTA1s/s320/visitors1.PNG" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;It turns out that Google was ranking me fairly high on some search terms, and when I noticed the increased traffic, I tried to leverage that a couple different ways. First, I succumbed briefly to temptation and put Adwords advertisements on the site for a couple weeks. Now, 4,046 is a nice number for a lone blogger who started out just writing about his kid, but it's not enough to pay the bills on click-throughs. So I took them down when my Adwords balance slowly crawled to the 7 cent mark.&lt;br /&gt;&lt;br /&gt;Next, I put up a couple different attempts to solicit feedback from visitors, which was about as popular with visitors as the ads. I got a total of 4 responses, one of which was just a smartass comment from a friend I work with, another was not honest feedback, but a solicitation to help grow a content farm.&lt;br /&gt;&lt;br /&gt;So I've taken down the "feedback" link, and turned off analytics, as it never really bought me anything other than some nebulous insight into what people search for, and how hard it is to get the attention of people looking for homework help, job interview answers, or instructions on gaming Facebook's PuzzleMaster coding puzzles... not that I look down on any of that. If you see how I solved a problem, maybe something will click and you'll be better off. If your goal is to cheat an assignment or test, well, good luck with that, but I doubt I'm improving anyone's chances of passing a class they weren't meant to. For now, "The Sky" is back to being just a plain old blog, where I write about whatever suits me, and visitors are welcome, but not the focus.&lt;br /&gt;&lt;br /&gt;Before turning off analytics, my most popular pages were:&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/_YH1HTjXGzNU/TTl_AKvvFJI/AAAAAAAADLU/Vq_YI7bMt68/s1600/visitors2.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="170" src="http://1.bp.blogspot.com/_YH1HTjXGzNU/TTl_AKvvFJI/AAAAAAAADLU/Vq_YI7bMt68/s320/visitors2.PNG" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;"Quest for Interesting" is a whimsical post that became popular only because it contains an image I created in MS Paint in about 10 minutes, which shows how to cross-multiply to compare fractions. For whatever reason, that image was for a few months the number one hit on Google Images for "cross multiplication". It currently fluctuates between 2nd and 3rd place, and still drives traffic here.&lt;br /&gt;&lt;br /&gt;"Solving the Facebook Gattaca Puzzle" is one of my solutions to a Facebook puzzle on their "careers" page. They use interesting puzzles involving optimization and datasets as a filtering tool for potential coders, which I think is a good idea. The number of people attempting to use search engines to game the system is phenomenal. To illustrate, here are the top 5 search terms used to find any page on my site:&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/_YH1HTjXGzNU/TTl_FYPQexI/AAAAAAAADLY/J0Du-w1YZM8/s1600/visitors3.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="137" src="http://3.bp.blogspot.com/_YH1HTjXGzNU/TTl_FYPQexI/AAAAAAAADLY/J0Du-w1YZM8/s320/visitors3.PNG" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;"Oldschool Mainframe/COBOL geek-out" is me going on at length about mainframe topics like RPF scripts in ROSCOE, compiling and linking from JCL, why EBCDIC is even a thing, and how no one "plays" on mainframes.&lt;br /&gt;&lt;br /&gt;The last, "Creating an Archimedean Spiral generator in Java using the NetBeans IDE" is one of several quick hacks of mine, this one attempting to be a basic introduction to using NetBeans, the basics of painting and the Java2D library, and polar math vs. Euclidean. Because it's fun. And I'm a geek who despairs not taking Calculus in high school, and not paying more attention in the math classes I did take.&lt;br /&gt;&lt;br /&gt;So, I have dismissed my Joab from his census-taking duties, before being punished by the almighty for my pride. Now it's back to just writing about stuff, without the marketing angle.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11885757-2797651215583651036?l=cautery.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://cautery.blogspot.com/feeds/2797651215583651036/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://cautery.blogspot.com/2011/01/4046.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11885757/posts/default/2797651215583651036'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11885757/posts/default/2797651215583651036'/><link rel='alternate' type='text/html' href='http://cautery.blogspot.com/2011/01/4046.html' title='4,046'/><author><name>Curtis Autery</name><uri>https://profiles.google.com/107677530285177731535</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh4.googleusercontent.com/--J3C_khtLqQ/AAAAAAAAAAI/AAAAAAAADT4/e2Qa5ncfAMk/s512-c/photo.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_YH1HTjXGzNU/TTl-5R4r7sI/AAAAAAAADLQ/NhnaHCiTA1s/s72-c/visitors1.PNG' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11885757.post-1654675462909664176</id><published>2011-01-16T10:16:00.003-05:00</published><updated>2011-01-16T10:23:24.657-05:00</updated><title type='text'>OSU Math Placement B-test, 1 of ???</title><content type='html'>A little experiment with Google Docs, Wikipedia's TeX engine for rendering math equations, and trying to help my wife pass her college math requirements.&lt;br /&gt;&lt;br /&gt;Where's that Check 21 2 of 2 I promised for yesterday? Still coming, I just need to get my inspiration back after a few, um, let's call them "challenging" weeks at my day job. Anyway, enjoy!&lt;br /&gt;&lt;br /&gt;&lt;a href="https://docs.google.com/document/d/1vnKHq4XlMU8lcXECH2QpqlEK8CUYZ09Nb0lUV0yp1_U/edit?hl=en"&gt;B Test study guide, question 1a&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11885757-1654675462909664176?l=cautery.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://cautery.blogspot.com/feeds/1654675462909664176/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://cautery.blogspot.com/2011/01/osu-math-placement-b-test-1-of.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11885757/posts/default/1654675462909664176'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11885757/posts/default/1654675462909664176'/><link rel='alternate' type='text/html' href='http://cautery.blogspot.com/2011/01/osu-math-placement-b-test-1-of.html' title='OSU Math Placement B-test, 1 of ???'/><author><name>Curtis Autery</name><uri>https://profiles.google.com/107677530285177731535</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh4.googleusercontent.com/--J3C_khtLqQ/AAAAAAAAAAI/AAAAAAAADT4/e2Qa5ncfAMk/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11885757.post-5526076001566885243</id><published>2011-01-09T18:21:00.001-05:00</published><updated>2011-01-09T18:21:37.418-05:00</updated><title type='text'>Adventures in Barretting</title><content type='html'>In an attempt to be funny, I made up the term “Barretting” to describe the act of driving to Tennessee to see my friend Chris Barrett.  My wife, Liberty, and I had some fun throughout the week using the term whenever it would fit.  “Well, we don’t need to stop at the store on Friday, we can get to that when we’re back from Barretting.”&lt;br /&gt;&lt;br /&gt;Chris introduced us to the “Flying Saucer” pub last year, which has a giant wall full of taps (possibly 20 or 30), as well as friendly wait-staff, not too intolerable prices, and a 100-beer tour that gets you some sort of free party there and your name on a plate if you finish it.  Liberty and I went once with Chris to the Memphis location, and have been twice to the one in Nashville while taking an impromptu “let’s see where this road takes us” vacation.&lt;br /&gt;&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;Chris used to live in Columbus like we do, but now works in western North Carolina.  Nashville is a good midpoint between us, so we planned to meet up this weekend and catch up for a few hours, and go our separate ways.  We did so, adding beers 10 through 12 to our tours (16 to 18 for Chris) and a good time was had by all.  But something happened on the way down that trumps that story.&lt;br /&gt;&lt;br /&gt;On Saturday morning we started our drive down, and by mid-afternoon, with the sun starting to dip low in preparation for nightfall, we found ourselves on I-65, a little South of Louisville Kentucky.  There was this idyllic moment as we drove down a stretch of road with rock structures on either side.  There were a couple clouds visible, contrasting the darkening blue sky, a flock of birds crossed the road in front of us, first horizontal, then swirling to a vertical diamond-shape as the crossed the opposite rock facing.  Liberty was sitting beside me, engrossed in knitting a new creation, and a local radio station was playing the Zeppelin song “In My Time of Dying”.&lt;br /&gt;&lt;br /&gt;And there it was.  Our life and responsibilities back home - to kids, house, school, and job - was a fiction that neither of us believed in.  The truth was the road, the song, the shawl, the birds, and the setting sun.  And the real truth: each other.&lt;br /&gt;&lt;br /&gt;I felt a sense of overwhelming bliss just driving around with the woman I love.  No matter what happened after we decided our fiction back home was real again, Liberty would be with me still, and hence, the world would be less bleak.&lt;br /&gt;&lt;br /&gt;In 71 days we will have been married for three years.  When we look at each other, we both know we found the right person.  The house may collapse, my job may give me the boot, the mounting school debt may be a beast difficult to slay, but that will all be illusion.  The real world will continue to be the birds serenading us before sunset, as we head down the road together.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11885757-5526076001566885243?l=cautery.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://cautery.blogspot.com/feeds/5526076001566885243/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://cautery.blogspot.com/2011/01/adventures-in-barretting.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11885757/posts/default/5526076001566885243'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11885757/posts/default/5526076001566885243'/><link rel='alternate' type='text/html' href='http://cautery.blogspot.com/2011/01/adventures-in-barretting.html' title='Adventures in Barretting'/><author><name>Curtis Autery</name><uri>https://profiles.google.com/107677530285177731535</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh4.googleusercontent.com/--J3C_khtLqQ/AAAAAAAAAAI/AAAAAAAADT4/e2Qa5ncfAMk/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11885757.post-2654143109774715052</id><published>2010-12-28T21:21:00.000-05:00</published><updated>2010-12-28T21:21:27.461-05:00</updated><title type='text'>Check 21 Java app - 1 of 2</title><content type='html'>Extracting data and images from Image Cash Letter files&lt;p class="section"&gt;Background&lt;/p&gt;&lt;p&gt;There are many terms relating to Image Cash Letter files: Check 21, DSTU X9.37, X9.100-187, and check truncation, to name a few.&lt;/p&gt;&lt;p&gt;"Check 21" refers to an act passed in 2003. From Wikipedia's "Check 21 Act" article:&lt;/p&gt;&lt;a name='more'&gt;&lt;/a&gt;&lt;blockquote&gt;&lt;p&gt;The Check Clearing for the 21st Century Act (or Check 21 Act) is a United States federal law, Pub.L. 108-100 ... The law allows the recipient of the original paper check to create a digital version of the original check—called a "substitute check," thereby eliminating the need for further handling of the physical document...&lt;/p&gt;&lt;p&gt;...Recently, Check 21 software providers have developed a "Virtual Check 21" system which allows online and offline merchants to create and submit demand draft documents to the bank of deposit. This process which combines remotely created checks (RCC) and Check 21 X9.37 files enables merchants to benefit from direct merchant-to-bank relationships, lower NSFs, and lower chargebacks.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;The last paragraph bears some resemblance to reality, but the article's footnote refers to just one of many companies capitalizing on the Check 21 act, and the lofty goal of direct merchant-to-bank relationships is not met due to the very fact of requiring a middle-man's software to manage the relationship.&lt;/p&gt;&lt;p&gt;The text of the act itself is available at &lt;a href="http://origin.www.gpo.gov/fdsys/pkg/PLAW-108publ100/pdf/PLAW-108publ100.pdf"&gt;this page&lt;/a&gt; on gpo.gov, the opening paragraph being:&lt;/p&gt;&lt;blockquote&gt;To facilitate check truncation by authorizing substitute checks, to foster innovation in the check collection system without mandating receipt of checks in electronic form, and to improve the overall efficiency of the Nation's payments system, and for other purposes.&lt;/blockquote&gt;&lt;p&gt;My interpretation is that the act clearly states that no one is forcing banks to receive checks electronically, but new law is being written allowing banks to use electronic copies (or photocopies, or re-prints) of checks in place of the originals at their discretion. Later text in the act provides for a future study to see how banks are adopting it, whether their profits are affected, whether it's useful, etc.&lt;/p&gt;&lt;p&gt;When the act was passed, ANSI published DSTU X9.37 "for Electronic Exchange of Check and Image Data". "DSTU" stands for "Draft Standard for Trial Use". "X9" is the banking arm of ANSI (where "X12" is the EDI arm). So basically in 2003 a trial standard was published for exchanging check images, with an evaluation of the standard to come later. That came in 2008, when ANSI published X9.100-180 for Canadian use, and X9.100-187 for use in the states, with minor alterations from the original standard.&lt;/p&gt;&lt;p class="section"&gt;Background on the file format&lt;/p&gt;&lt;p&gt;An image cash letter is designed for mainframe use. It is record-based, with a 4-byte word at the beginning of each record indicating its length. It contains mixed text and image data, with the text part being in the EBCDIC character set, and the image part being TIFF data. If you are a small business that happens to have a mainframe laying around, this is good news. For merchants living in the real world, not so much.&lt;/p&gt;&lt;p&gt;The file format poses a number of challenges for Windows, Mac, and Unix programmers. Unless you are versed in the mainframe/AS400 world, variable length records with length bytes and the EBCDIC character set are probably unfamiliar to you. Once you figure out EBCDIC translation, you then need to tackle converting the record-length header to a number, which, unless you are in the habit of programming at a "bits on the wire" level (checksums, for instance), you've probably never needed to do before.&lt;/p&gt;&lt;p&gt;Once you tackle record lengths and EBCDIC and start playing with the image data, you'll quickly realize a simple fact that you never noticed before: nothing supports TIFF images. Your web browser doesn't display them, and your programming language doesn't have support for them in its native image library. You either need to invest more research time into finding a TIFF library and learning its API, or just dump image data to individual files each time you come across it, leaving your end-user with a large collection of files to open by hand. This would lead to the following uncomfortable conversation:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;User: "So I'll have a bunch of image files representing checks that I have to click on one at a time to view?"&lt;/p&gt;&lt;p&gt;You: "Right."&lt;/p&gt;&lt;p&gt;User: "And there's no way to correlate those back to the text data?"&lt;/p&gt;&lt;p&gt;You: "Right."&lt;/p&gt;&lt;p&gt;User: "Well, do I at least get the front and back of checks in the same image?"&lt;/p&gt;&lt;p&gt;You: "Um, well... no."&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;Because of these mild but unfamiliar technical hurdles, the third-party app that supports Check21 files looks more attractive to a CTO than watching the internal IT team bumble around trying to build an app. In fact, an industry has popped up around these files, costing companies money in vendor lock-in that they tried to save by converting from ACH files and mailing checks, all in the name of getting money to the bank a little faster. Not a good situation.&lt;/p&gt;&lt;p class="section"&gt;Impetus for this entry&lt;/p&gt;&lt;p&gt;In July of '09, I published a &lt;a href="http://www.perlmonks.org/?node_id=784982"&gt;perl script&lt;/a&gt; that does just what I described above. It takes a Check 21 file and prints its ASCII-translated text to STDOUT, and saves the image data into individual TIFF files. It was intended only as a quick experiment with handling the Check21 file format, but its Google rank grew until it was on the front page for searches like "x9.37 parse". This is odd because the script is a quick hack (in fact the first comment on it complains about its lack of exception handling) that I threw together in about 30 minutes, so it's possible that there aren't many "open" apps available that handle this file type.&lt;/p&gt;&lt;p&gt;Recently someone asked me for help with a problem using the extractor. Between my script's unexpected high search rank, and the fact that questions about my tech posts are pretty rare, one can reasonably assume that Check21 files are currently a big deal in "the wild", and knowledge about them is pretty slim. Assuming that to be the case, this post is the first in a two-part Check21 java series exploring the file format, and concerns with parsing, displaying, and creating Check21 files. This first post shows how to write an extractor similar to my perl script, but in Java.&lt;/p&gt;&lt;p class="section"&gt;Bits on the wire&lt;/p&gt;&lt;p&gt;Let's start by opening a file in a hex editor to see what it looks like. The file I'm using is available on &lt;a href="http://www.x937.com/SampleX937File.htm"&gt;this page&lt;/a&gt; on x937.com, a private site run by software developer Norman Graham.&lt;/p&gt;&lt;p&gt;&lt;img src="http://lh6.ggpht.com/_YH1HTjXGzNU/TQ4jAJnvgDI/AAAAAAAADKQ/ebvq2ttara4/s800/chk21HexDump.PNG"&gt;&lt;/p&gt;&lt;p&gt;To the untrained eye, this looks like so much garbage. To those more mainframe-savvy, the top section peppered with at-signs and letters with diacritics means you're probably looking at EBCDIC rendered with an ASCII viewer. To the image geeks, the II* on line 210h is indicative of the beginning of TIFF data.&lt;/p&gt;&lt;p&gt;The first four bytes combine as one hex number: 0x00000050, or decimal 80, meaning the next 80 bytes constitute a record. The next few bytes (F0, F1, F0, F3) are EBCDIC representations of numeric digits, as EBCDIC places 0 through 9 in hex F0 through hex F9 (where ASCII has them at hex 30 through 39), so the first four characters of the record should be "0103".&lt;/p&gt;&lt;p class="section"&gt;Tracing through the JRE&lt;/p&gt;&lt;p&gt;With those basic assumptions, let's throw some code at it and see what we get. The record length header can be turned into a number with very little effort. The Java class javax.imageio.stream.ImageInputStreamImpl provides the following method:&lt;/p&gt;&lt;pre&gt;&lt;br /&gt;public int readInt() throws IOException {&lt;br /&gt;    if (read(byteBuf, 0, 4) &amp;lt; 0) {&lt;br /&gt;    throw new EOFException();&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    if (byteOrder == ByteOrder.BIG_ENDIAN) {&lt;br /&gt;        return&lt;br /&gt;            (((byteBuf[0] &amp;amp; 0xff) &amp;lt;&amp;lt; 24) | ((byteBuf[1] &amp;amp; 0xff) &amp;lt;&amp;lt; 16) |&lt;br /&gt;             ((byteBuf[2] &amp;amp; 0xff) &amp;lt;&amp;lt;  8) | ((byteBuf[3] &amp;amp; 0xff) &amp;lt;&amp;lt;  0));&lt;br /&gt;    } else {&lt;br /&gt;        return&lt;br /&gt;            (((byteBuf[3] &amp;amp; 0xff) &amp;lt;&amp;lt; 24) | ((byteBuf[2] &amp;amp; 0xff) &amp;lt;&amp;lt; 16) |&lt;br /&gt;             ((byteBuf[1] &amp;amp; 0xff) &amp;lt;&amp;lt;  8) | ((byteBuf[0] &amp;amp; 0xff) &amp;lt;&amp;lt;  0));&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;p&gt;The variable "byteBuf" is a byte array. Four bytes are read into it, and using bitwise math, a single 32-bit integer is returned. This is a less processor heavy version of looping through the byte array, multiplying by 256, and adding the next byte.&lt;/p&gt;&lt;p&gt;So if I open the file as an ImageInputStream, I can use readInt() to grab record lengths and move forward in the file. Next, the EBCDIC to ASCII translation. The java.lang.String class has a constructor that accepts a string input declaring the character set:&lt;/p&gt;&lt;p&gt;public String(byte bytes[], String charsetName)&lt;/p&gt;&lt;p&gt;This is ultimately a convenience method for java.lang.StringCoding.decode(), which looks up a CharSet class and returns a new String by converting each byte using a translation table. For EBCDIC translation, specify "Cp1047" as the character set name, and Java will find the class sun.nio.cs.ext.IBM1047, which contains this table:&lt;/p&gt;&lt;pre&gt;&lt;br /&gt; private final static String byteToCharTable =&lt;br /&gt; &lt;br /&gt;     "\u00D8\u0061\u0062\u0063\u0064\u0065\u0066\u0067" +     // 0x80 - 0x87&lt;br /&gt;     "\u0068\u0069\u00AB\u00BB\u00F0\u00FD\u00FE\u00B1" +     // 0x88 - 0x8F&lt;br /&gt;     "\u00B0\u006A\u006B\u006C\u006D\u006E\u006F\u0070" +     // 0x90 - 0x97&lt;br /&gt;     "\u0071\u0072\u00AA\u00BA\u00E6\u00B8\u00C6\u00A4" +     // 0x98 - 0x9F&lt;br /&gt;     "\u00B5\u007E\u0073\u0074\u0075\u0076\u0077\u0078" +     // 0xA0 - 0xA7&lt;br /&gt;     "\u0079\u007A\u00A1\u00BF\u00D0\u005B\u00DE\u00AE" +     // 0xA8 - 0xAF&lt;br /&gt;     "\u00AC\u00A3\u00A5\u00B7\u00A9\u00A7\u00B6\u00BC" +     // 0xB0 - 0xB7&lt;br /&gt;     "\u00BD\u00BE\u00DD\u00A8\u00AF\u005D\u00B4\u00D7" +     // 0xB8 - 0xBF&lt;br /&gt;     "\u007B\u0041\u0042\u0043\u0044\u0045\u0046\u0047" +     // 0xC0 - 0xC7&lt;br /&gt;     "\u0048\u0049\u00AD\u00F4\u00F6\u00F2\u00F3\u00F5" +     // 0xC8 - 0xCF&lt;br /&gt;     "\u007D\u004A\u004B\u004C\u004D\u004E\u004F\u0050" +     // 0xD0 - 0xD7&lt;br /&gt;     "\u0051\u0052\u00B9\u00FB\u00FC\u00F9\u00FA\u00FF" +     // 0xD8 - 0xDF&lt;br /&gt;     "\\\u00F7\u0053\u0054\u0055\u0056\u0057\u0058" +     // 0xE0 - 0xE7&lt;br /&gt;     "\u0059\u005A\u00B2\u00D4\u00D6\u00D2\u00D3\u00D5" +     // 0xE8 - 0xEF&lt;br /&gt;     "\u0030\u0031\u0032\u0033\u0034\u0035\u0036\u0037" +     // 0xF0 - 0xF7&lt;br /&gt;     "\u0038\u0039\u00B3\u00DB\u00DC\u00D9\u00DA\u009F" +     // 0xF8 - 0xFF&lt;br /&gt;     "\u0000\u0001\u0002\u0003\u009C\t\u0086\u007F" +     // 0x00 - 0x07&lt;br /&gt;     "\u0097\u008D\u008E\u000B\f\r\u000E\u000F" +     // 0x08 - 0x0F&lt;br /&gt;     "\u0010\u0011\u0012\u0013\u009D\n\b\u0087" +     // 0x10 - 0x17&lt;br /&gt;     "\u0018\u0019\u0092\u008F\u001C\u001D\u001E\u001F" +     // 0x18 - 0x1F&lt;br /&gt;     "\u0080\u0081\u0082\u0083\u0084\u0085\u0017\u001B" +     // 0x20 - 0x27&lt;br /&gt;     "\u0088\u0089\u008A\u008B\u008C\u0005\u0006\u0007" +     // 0x28 - 0x2F&lt;br /&gt;     "\u0090\u0091\u0016\u0093\u0094\u0095\u0096\u0004" +     // 0x30 - 0x37&lt;br /&gt;     "\u0098\u0099\u009A\u009B\u0014\u0015\u009E\u001A" +     // 0x38 - 0x3F&lt;br /&gt;     "\u0020\u00A0\u00E2\u00E4\u00E0\u00E1\u00E3\u00E5" +     // 0x40 - 0x47&lt;br /&gt;     "\u00E7\u00F1\u00A2\u002E\u003C\u0028\u002B\u007C" +     // 0x48 - 0x4F&lt;br /&gt;     "\u0026\u00E9\u00EA\u00EB\u00E8\u00ED\u00EE\u00EF" +     // 0x50 - 0x57&lt;br /&gt;     "\u00EC\u00DF\u0021\u0024\u002A\u0029\u003B\u005E" +     // 0x58 - 0x5F&lt;br /&gt;     "\u002D\u002F\u00C2\u00C4\u00C0\u00C1\u00C3\u00C5" +     // 0x60 - 0x67&lt;br /&gt;     "\u00C7\u00D1\u00A6\u002C\u0025\u005F\u003E\u003F" +     // 0x68 - 0x6F&lt;br /&gt;     "\u00F8\u00C9\u00CA\u00CB\u00C8\u00CD\u00CE\u00CF" +     // 0x70 - 0x77&lt;br /&gt;     "\u00CC\u0060\u003A\u0023\u0040\'\u003D\"";     // 0x78 - 0x7F&lt;br /&gt; &lt;/pre&gt;&lt;p class="section"&gt;Client code&lt;/p&gt;&lt;p&gt;Here is a quick test class to open the file as an ImageInputStream, and attempt to find the first record's length, read that many bytes, and translate to ASCII.&lt;/p&gt;&lt;pre&gt;&lt;br /&gt;package com.cea.check21;&lt;br /&gt;&lt;br /&gt;import java.io.File;&lt;br /&gt;import java.io.IOException;&lt;br /&gt;import javax.imageio.stream.FileImageInputStream;&lt;br /&gt;&lt;br /&gt;public class Test {&lt;br /&gt;  public static void main(String[] args) throws IOException {&lt;br /&gt;    FileImageInputStream is = new FileImageInputStream(new File("one.x9"));&lt;br /&gt;    int recLen = is.readInt();&lt;br /&gt;    System.out.println("Record length: " + recLen);&lt;br /&gt;    byte[] rec = new byte[recLen];&lt;br /&gt;    is.read(rec);&lt;br /&gt;    System.out.println(new String(rec, "Cp1047"));&lt;br /&gt;    is.close();&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;p&gt;After running that, the following output is displayed:&lt;/p&gt;&lt;pre&gt;&lt;br /&gt;Record length: 80&lt;br /&gt;0103T113000609111012822200408052030NUS BANKO NORM     First Bank of NormAUS     &lt;br /&gt;&lt;/pre&gt;&lt;p&gt;This looks good. 80 matches the record length we calculated from the hex dump, and the first four characters are "0103" as we expected. The rest of the line looks like numbers and semi-readable text, so we're definitely on the right track.&lt;/p&gt;&lt;p&gt;Now let's flesh out the class a little by iterating over each record and see what happens. The new class:&lt;/p&gt;&lt;pre&gt;&lt;br /&gt;package com.cea.check21;&lt;br /&gt;&lt;br /&gt;import java.io.File;&lt;br /&gt;import java.io.IOException;&lt;br /&gt;import javax.imageio.stream.FileImageInputStream;&lt;br /&gt;&lt;br /&gt;public class Test {&lt;br /&gt;  public static void main(String[] args) throws IOException {&lt;br /&gt;    FileImageInputStream is = new FileImageInputStream(new File("one.x9"));&lt;br /&gt;    int recLen;&lt;br /&gt;    while ((recLen = is.readInt()) &amp;gt; 0) {&lt;br /&gt;      System.out.println("Record length: " + recLen);&lt;br /&gt;      byte[] rec = new byte[recLen];&lt;br /&gt;      is.read(rec);&lt;br /&gt;      System.out.println(new String(rec, "Cp1047"));&lt;br /&gt;    }&lt;br /&gt;    is.close();&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;p&gt;The output:&lt;/p&gt;&lt;p&gt;&lt;img src="http://lh6.ggpht.com/_YH1HTjXGzNU/TRoN1oNduyI/AAAAAAAADKs/h-2u2EAkg2E/s800/chk21RawTranslate.PNG"/&gt;&lt;/p&gt;&lt;p&gt;...and finally:&lt;/p&gt;&lt;pre&gt;&lt;br /&gt;Exception in thread "main" java.io.EOFException&lt;br /&gt; at javax.imageio.stream.ImageInputStreamImpl.readInt(ImageInputStreamImpl.java:237)&lt;br /&gt; at com.cea.check21.Test.main(Test.java:11)&lt;br /&gt;&lt;/pre&gt;&lt;p&gt;OK, so a closer inspection of the API would have shown that readInt() throws an exception when it reads past the end of the file, but other than that, we're good up to the point where the image data is encountered. The image data occurs where the spec says it will: on the 52 record, at column 118.&lt;/p&gt;&lt;p&gt;So to finish up text conversion, we need to do two things. First, handle EOF more gracefully, and second, print only the first 117 characters of 52 records.&lt;/p&gt;&lt;pre&gt;&lt;br /&gt;package com.cea.check21;&lt;br /&gt;&lt;br /&gt;import java.io.File;&lt;br /&gt;import java.io.IOException;&lt;br /&gt;import javax.imageio.stream.FileImageInputStream;&lt;br /&gt;&lt;br /&gt;public class Test {&lt;br /&gt;  public static void main(String[] args) throws IOException {&lt;br /&gt;    File file = new File("one.x9");&lt;br /&gt;    FileImageInputStream is = new FileImageInputStream(file);&lt;br /&gt;    &lt;br /&gt;    while (is.getStreamPosition() &amp;lt; file.length()) {&lt;br /&gt;      int recLen = is.readInt();&lt;br /&gt;      byte[] rec = new byte[recLen];&lt;br /&gt;      is.read(rec);&lt;br /&gt;      String recNum = new String(rec, 0, 2, "Cp1047");&lt;br /&gt;      if (recNum.equals("52")) System.out.println(new String(rec, 0, 117, "Cp1047"));&lt;br /&gt;      else System.out.println(new String(rec, "Cp1047"));&lt;br /&gt;    }&lt;br /&gt;    is.close();&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;p&gt;Which outputs:&lt;/p&gt;&lt;pre&gt;&lt;br /&gt;0103T113000609111012822200408052030NUS BANKO NORM     First Bank of NormAUS     &lt;br /&gt;100111300060911101282220060109200601092030FG16410001nfg MANAGEMENTxxxxxxxxxxC0  &lt;br /&gt;200109100002211101282220070315200703150020300001000104053000196                 &lt;br /&gt;25           1104 113000609    123456789012345/00000004320999987267040  G01Y000 &lt;br /&gt;50111101282220060109000002206260000                     0                       &lt;br /&gt;5211101282220060109040991400001                                                 &lt;br /&gt;    0                0000000000018628&lt;br /&gt;50111101282220060109000002206261000                     0                       &lt;br /&gt;5211101282220060109040991400001                                                 &lt;br /&gt;    0                0000000000023720&lt;br /&gt;70000100000000043200000000043200001                                             &lt;br /&gt;900000010000000100000000000432000000001 l NORMPOINTE BANK20060109               &lt;br /&gt;9900000100000001000000010000000000000432NORMAN AGEMENT2145085900                &lt;br /&gt;&lt;/pre&gt;&lt;p&gt;Success!&lt;/p&gt;&lt;p&gt;The last step for a simple extractor is to dump TIFF data into files. We'll iterate a counter for each 52 record, and name the files img(count).tiff. Additionally, I've added some handling for specifying file names on the commandline, and finding their working directories. Lastly the class name has been changed to something more descriptive than "Test".&lt;/p&gt;&lt;p&gt;The final code:&lt;/p&gt;&lt;pre&gt;&lt;br /&gt;package com.cea.check21;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;import java.io.File;&lt;br /&gt;import java.io.IOException;&lt;br /&gt;&lt;br /&gt;import javax.imageio.stream.FileImageInputStream;&lt;br /&gt;import javax.imageio.stream.FileImageOutputStream;&lt;br /&gt;&lt;br /&gt;public class Extractor {&lt;br /&gt;  public static void main(String[] args) throws IOException {&lt;br /&gt;    &lt;br /&gt;    if (args.length &amp;lt; 1) {&lt;br /&gt;      System.out.println("Usage: java Extractor &amp;lt;checkImageFile&amp;gt;");&lt;br /&gt;      System.exit(0);&lt;br /&gt;    }&lt;br /&gt;    &lt;br /&gt;    File file = new File(args[0]);&lt;br /&gt;    FileImageInputStream is = new FileImageInputStream(file);&lt;br /&gt;    int tiffCount = 0;&lt;br /&gt;    String workDir = file.getParent();&lt;br /&gt;    if (workDir == null) workDir = ".";&lt;br /&gt;    String sep = File.separator;&lt;br /&gt;    &lt;br /&gt;    while(is.getStreamPosition() &amp;lt; file.length()) {&lt;br /&gt;      int recLen = is.readInt();&lt;br /&gt;      byte[] rec = new byte[recLen];&lt;br /&gt;      is.read(rec);&lt;br /&gt;      String recNum = new String(rec, 0, 2, "Cp1047");&lt;br /&gt;      if (recNum.equals("52")) {&lt;br /&gt;        System.out.println(new String(rec, 0, 117, "Cp1047"));&lt;br /&gt;        tiffCount++;&lt;br /&gt;        String numberPart = String.valueOf(tiffCount);&lt;br /&gt;        while (numberPart.length() &amp;lt; 4) numberPart = "0" + numberPart;&lt;br /&gt;        String fileName = workDir + sep + "img" + numberPart + ".tiff";&lt;br /&gt;        FileImageOutputStream out = new FileImageOutputStream(new File(fileName));&lt;br /&gt;        out.write(rec, 117, rec.length - 117);&lt;br /&gt;        out.close();&lt;br /&gt;      } else System.out.println(new String(rec, "Cp1047"));&lt;br /&gt;    }&lt;br /&gt;    is.close();&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;p&gt;The text output is the same, and two image files are created in the directory containing one.x9:&lt;/p&gt;&lt;p&gt;&lt;img src="http://lh4.ggpht.com/_YH1HTjXGzNU/TRoa22tGPoI/AAAAAAAADK0/RzeKdnL83M0/s800/check21DirList.PNG"/&gt;&lt;/p&gt;&lt;p&gt;And they look like this:&lt;/p&gt;&lt;p&gt;&lt;img src="http://lh6.ggpht.com/_YH1HTjXGzNU/TQ4eukHF7KI/AAAAAAAADKE/_BUkalQKXy0/s640/chk21TestFront.PNG"/&gt;&lt;br/&gt;&lt;img src="http://lh6.ggpht.com/_YH1HTjXGzNU/TQ4eu28ELjI/AAAAAAAADKI/hQAhs8jAk8I/s640/chk21TestBack.PNG"/&gt;&lt;p class="section"&gt;Next steps&lt;/p&gt;&lt;p&gt;This Java class is useful only so far as exploring the Check 21 file format, but provides very little real-world utility. What's needed is an app that can display check information more intelligently, including breaking down the record information into something meaningful, and displaying the front and back of a check alongside that information.&lt;/p&gt;&lt;p&gt;To do that, we'll need to figure out how to turn TIFF data into a Java Image object, and to explore the X9.100-187 record layouts in a little more detail.&lt;/p&gt;&lt;p&gt;I'm giving myself an arbitrary deadline of January 15, 2011 to post my progress with that app. Tune in then!&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11885757-2654143109774715052?l=cautery.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://cautery.blogspot.com/feeds/2654143109774715052/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://cautery.blogspot.com/2010/12/check-21-java-app-1-of-2.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11885757/posts/default/2654143109774715052'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11885757/posts/default/2654143109774715052'/><link rel='alternate' type='text/html' href='http://cautery.blogspot.com/2010/12/check-21-java-app-1-of-2.html' title='Check 21 Java app - 1 of 2'/><author><name>Curtis Autery</name><uri>https://profiles.google.com/107677530285177731535</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh4.googleusercontent.com/--J3C_khtLqQ/AAAAAAAAAAI/AAAAAAAADT4/e2Qa5ncfAMk/s512-c/photo.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh6.ggpht.com/_YH1HTjXGzNU/TQ4jAJnvgDI/AAAAAAAADKQ/ebvq2ttara4/s72-c/chk21HexDump.PNG' height='72' width='72'/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11885757.post-6951546880157654499</id><published>2010-12-16T18:29:00.005-05:00</published><updated>2010-12-16T22:30:40.558-05:00</updated><title type='text'>Java greyscale conversion</title><content type='html'>&lt;p&gt;Using Java, how do you convert a color image to greyscale? The most straightforward answer I've found was on &lt;a href="http://blog.codebeach.com/2008/03/convert-color-image-to-gray-scale-image.html"&gt;Code Beach&lt;/a&gt;, and is as follows:&lt;/p&gt;&lt;a name='more'&gt;&lt;/a&gt;&lt;pre&gt;&lt;br /&gt;BufferedImage image = new BufferedImage(width, height,  &lt;br /&gt;    BufferedImage.TYPE_BYTE_GRAY);  &lt;br /&gt;Graphics g = image.getGraphics();  &lt;br /&gt;g.drawImage(colorImage, 0, 0, null);  &lt;br /&gt;g.dispose();&lt;br /&gt;&lt;/pre&gt;&lt;p&gt;Create a BufferedImage of type TYPE_BYTE_GRAY, and grab its Graphics object. Draw the color image onto it. Fin.&lt;/p&gt;&lt;p&gt;The described method uses a small amount of client code for a fast, good quality greyscale conversion, but a lot goes on under the hood to make this happen in the Java runtime jar. Tracing through the code, eventually you arrive at the getDataElements method of java.awt.image.IndexColorModel, and the following line is executed over every pixel in the image:&lt;/p&gt;&lt;pre&gt;&lt;br /&gt;int gray = (int) (red*77 + green*150 + blue*29 + 128)/256;&lt;br /&gt;&lt;/pre&gt;&lt;p&gt;This takes the red, green, and blue components of a pixel's composite color, and calculates its relative brightness (on a scale here that is, oddly, between 0.5 and 1.5), and later uses this value to mix a grey color of the same brightness for the replacement pixel.&lt;/p&gt;&lt;p class=section&gt;A quick (and admittedly non-expert) course on color theory as it relates to computers:&lt;/p&gt;&lt;p&gt;Your computer screen does not have pixels that can change color. For every perceived pixel, there are three "lights" (for simplicity's sake) very close together that are the primary colors red, green, and blue. When all three lights are off, you see black. When they are all turned on at full brightness, you see white. Mixing the lights at different power levels produces very close to the full range of colors that our eyes can perceive.&lt;/p&gt;&lt;p&gt;The lights have 256 power levels, for a total color space of 16,777,216 colors (256 cubed). Black is seen when red, green, and blue are all 0; white when they are all 255. There are also 254 shades of grey, seen when the red, green, and blue values all match and are between 1 and 254. This brings us back to the brightness equation above: find a color's brightness, scale it between 0 and 255, and you can make a grey with the same brightness by setting the red, green, and blue of the pixel to that value. Repeat for all the pixels in an image and you have a greyscale copy of it.&lt;/p&gt;&lt;p class=section&gt;Whence the 77/150/29 ratio?&lt;/p&gt;&lt;p&gt;In 1982, when I was 11 years old and a holy terror on my 6th grade teachers, the International Telecommunication Union published Recommendation 601 for transmitting video signals digitally. In it the following luminance (brightness) equation is mentioned in section 2.1:&lt;/p&gt;&lt;pre&gt;&lt;br /&gt;E'&lt;sub&gt;Y&lt;/sub&gt; = 0.299 E'&lt;sub&gt;R&lt;/sub&gt; + 0.587 E'&lt;sub&gt;G&lt;/sub&gt; + 0.114 E'&lt;sub&gt;B&lt;/sub&gt;&lt;br /&gt;&lt;/pre&gt;&lt;p&gt;These numbers add up to 1, where 77, 150, and 29 add up to 256. Dividing each of those by 256 gives a close approximation to the Rec 601 ratios, with the added benefit of using only integers and dividing by a power of 2, which computer processors are very good at. These specific ratios of red, green, and blue to determine luminance can be traced back to the XYZ color space, created by the International Commission on Illumination (CIE) in 1931, which in turn references work by James Clerk Maxwell (Experiments on colour, as perceived by the eye, with remarks on colour-blindness [1855]) which itself references work done by none other than Isaac Newton in the book Opticks (1704) which talks about breaking light into its component parts using prisms, discussing compound vs. homogeneal light, building lenses and telescopes, etc.&lt;/p&gt;&lt;p&gt;I won't attempt a full analysis of all these works to trace the source of the numbers, but suffice to say in the 19th century, people stared at colored lights and twisted knobs to match colors, and scientists wrote down the final settings. After some math and repeated experiments, it was determined that green light looks pretty damned bright to us, red a little less, and blue less than that.&lt;/p&gt;&lt;p class=section&gt;Are there better ratios to create a crisper greyscale image?&lt;/p&gt;&lt;p&gt;In 1993, the ITU published Rec 709, a standard for modern HDTV systems. In section 4.2, the recommended luminance formula for the 1250/50/2:1 scanning standard used the old Rec 601 values. For 1125/60/2:1 (1080i) scanning, new values are recommended:&lt;/p&gt;&lt;pre&gt;&lt;br /&gt;E'&lt;sub&gt;Y&lt;/sub&gt; = 0.2126 E'&lt;sub&gt;R&lt;/sub&gt; + 0.7152 E'&lt;sub&gt;G&lt;/sub&gt; + 0.0722 E'&lt;sub&gt;B&lt;/sub&gt;&lt;br /&gt;&lt;/pre&gt;&lt;p&gt;That section's footnote says "The coefficients for the equations have been calculated following the rules laid down in SMPTE RP177-1993." This refers to a 1993 publication called "Derivation of Basic Television Color Equations" by the Society of Motion Picture and Television Engineers.&lt;/p&gt;&lt;p&gt;...and for the low cost of $50, I can buy the paper and see what their math was. From what I gather from other sources, the new calculations change when Gamma levels are applied, with the result being values better lined up with the 1931 XYZ color space. Finding out more isn't worth $50 to me. Some hoodoo is done with the gamma to produce a better luminance value - I'll take their word for it.&lt;/p&gt;&lt;p class=section&gt;Can I use the new values to greyscale an image in Java.&lt;/p&gt;&lt;p&gt;Yes. Instead of using Graphics.drawImage and letting Java figure out what to do with the pixels, you have to get your hands a little dirtier and work with PixelGrabbers, bitwise operators and integer arrays. But it turns out to not be that difficult.&lt;/p&gt;&lt;p&gt;Colors are defined in Java as 32-bit integers in four octets: alpha(opacity), red, green, and blue. For example, 0xff007f00 would be an opaque hunter-green, with the alpha set to ff(255) and the green set to 7f(127), with red and blue at 0. To take this integer value and pull out only the green part, you can either create a Color object and call its getGreen() method, or not waste the construction overhead and just do the math: right-shift 8 bits, then bitwise "and" the value to 0xff to zero out all but the last octet, with a statement like this:&lt;/p&gt;&lt;pre&gt;&lt;br /&gt;int g = (color &amp;gt;&amp;gt;  8) &amp;amp; 0xff;&lt;br /&gt;&lt;/pre&gt;&lt;p&gt;Getting the red value is similar, just bitshift 16 places instead 8, and with blue no bitshift is needed, just the bitwise "and".&lt;/p&gt;&lt;pre&gt;&lt;br /&gt;int r = (color &amp;gt;&amp;gt; 16) &amp;amp; 0xff;&lt;br /&gt;int g = (color &amp;gt;&amp;gt;  8) &amp;amp; 0xff;&lt;br /&gt;int b = (color      ) &amp;amp; 0xff;&lt;br /&gt;&lt;/pre&gt;&lt;p&gt;These values can then be used to construct a luminance value by plugging them into the Rec 709 numbers:&lt;/p&gt;&lt;pre&gt;&lt;br /&gt;int lum = (int) ((double)r * 0.2126 + (double)g * 0.7152 + (double)b * 0.0722);&lt;br /&gt;&lt;/pre&gt;&lt;p&gt;And finally a grey can be constructed by creating an integer whose red, green, and blue values all match the luminance number. This can be done with little overhead by left-shifting and using the bitwise "or" function:&lt;/p&gt;&lt;pre&gt;&lt;br /&gt;int greyColor = (lum &amp;lt;&amp;lt; 16) | (lum &amp;lt;&amp;lt; 8) | lum;&lt;br /&gt;&lt;/pre&gt;&lt;p&gt;The sample code below is a method that can be called specifying which luminance standard to use. A PixelGrabber grabs all the RGB values and sticks them into an integer array. Then the array is iterated over, converting all the colors to matching greys, and finally updating a BufferedImage with the new pixels:&lt;/p&gt;&lt;pre&gt;&lt;br /&gt;private Image greyScale(int mode, Image sourceImage) {&lt;br /&gt;  double redLum, greenLum, blueLum;&lt;br /&gt;  switch(mode) {&lt;br /&gt;  case 601:&lt;br /&gt;    redLum   = 0.299;&lt;br /&gt;    greenLum = 0.587;&lt;br /&gt;    blueLum  = 0.114;&lt;br /&gt;    break;&lt;br /&gt;  case 709:&lt;br /&gt;    redLum   = 0.2126;&lt;br /&gt;    greenLum = 0.7152;&lt;br /&gt;    blueLum  = 0.0722;&lt;br /&gt;    break;&lt;br /&gt;  default: return null;&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  int w = sourceImage.getWidth(null);&lt;br /&gt;  int h = sourceImage.getHeight(null);&lt;br /&gt;  int[] pixels = new int[w * h];&lt;br /&gt;  &lt;br /&gt;  PixelGrabber pg = new PixelGrabber(sourceImage, 0, 0, w, h, pixels, 0, w);&lt;br /&gt;  try {&lt;br /&gt;    pg.grabPixels();&lt;br /&gt;  } catch (InterruptedException e) {&lt;br /&gt;    e.printStackTrace();&lt;br /&gt;    return null;&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  BufferedImage greyImage = new BufferedImage(w, h, BufferedImage.TYPE_3BYTE_BGR);&lt;br /&gt;&lt;br /&gt;  int offset = 0;&lt;br /&gt;  for(int y = 0; y &amp;lt; h; y++) {&lt;br /&gt;    for (int x = 0; x &amp;lt; w; x++) {&lt;br /&gt;      int color = pixels[offset++];&lt;br /&gt;      int r = (color &amp;gt;&amp;gt; 16) &amp;amp; 0xff;&lt;br /&gt;      int g = (color &amp;gt;&amp;gt;  8) &amp;amp; 0xff;&lt;br /&gt;      int b = (color      ) &amp;amp; 0xff;&lt;br /&gt;      int lum = (int) ((double)r * redLum + (double)g * greenLum + (double)b * blueLum);&lt;br /&gt;      int greyColor = (lum &amp;lt;&amp;lt; 16) | (lum &amp;lt;&amp;lt; 8) | lum;&lt;br /&gt;      greyImage.setRGB(x, y, greyColor);&lt;br /&gt;    }&lt;br /&gt;  }&lt;br /&gt;  return greyImage;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;p&gt;Here are a couple screengrabs from an app using this method to compare the original and greyscaled images.&lt;/p&gt;&lt;p&gt;First using the 601 standard:&lt;br/&gt;&lt;img src="http://lh3.ggpht.com/_YH1HTjXGzNU/TQpoCrWuiUI/AAAAAAAADJs/n12ue6OBzWo/s800/greyscale_601.PNG"&gt;&lt;/p&gt;&lt;p&gt;Then the 709:&lt;br/&gt;&lt;img src="http://lh3.ggpht.com/_YH1HTjXGzNU/TQpoDDwoKXI/AAAAAAAADJw/XJ3-u7TSy9c/s800/greyscale_709.PNG"&gt;&lt;/p&gt;&lt;p&gt;The difference is subtle, but the print on Scout's dress is a little darker and better defined in the 709 image. And with that anecdotal and completely subjective comparison, I declare the 709 formula the better of the two. On a whim, I shot Oracle an email about the IndexColorModel class using the outdated formula, showing a simple change to use the updated one:&lt;/p&gt;&lt;pre&gt;&lt;br /&gt;int gray = (int) (red*55 + green*183 + blue*18 + 128)/256;&lt;br /&gt;&lt;/pre&gt;&lt;p&gt;I doubt that will come to anything, but, you know, it was worth at least mentioning it to them. If I get the added bonus of my line of code in the official rt.jar, so much the better.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11885757-6951546880157654499?l=cautery.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://cautery.blogspot.com/feeds/6951546880157654499/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://cautery.blogspot.com/2010/12/java-greyscale-conversion.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11885757/posts/default/6951546880157654499'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11885757/posts/default/6951546880157654499'/><link rel='alternate' type='text/html' href='http://cautery.blogspot.com/2010/12/java-greyscale-conversion.html' title='Java greyscale conversion'/><author><name>Curtis Autery</name><uri>https://profiles.google.com/107677530285177731535</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh4.googleusercontent.com/--J3C_khtLqQ/AAAAAAAAAAI/AAAAAAAADT4/e2Qa5ncfAMk/s512-c/photo.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh3.ggpht.com/_YH1HTjXGzNU/TQpoCrWuiUI/AAAAAAAADJs/n12ue6OBzWo/s72-c/greyscale_601.PNG' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11885757.post-5930073477840759254</id><published>2010-11-20T09:04:00.006-05:00</published><updated>2010-11-20T09:13:22.157-05:00</updated><title type='text'>Roman numerals redux</title><content type='html'>&lt;p&gt;A simple JavaScript function to validate Roman numerals, convert them to Arabic numbers, and back again.&lt;/p&gt;&lt;p&gt;I checked Google Analytics this morning and noticed I've been getting a lot of hits on my &lt;a href="http://cautery.blogspot.com/2010/10/javascript-roman-numeral-calculator.html"&gt;October 9 entry&lt;/a&gt; on JavaScript Roman numeral conversion. In brief, I wrote a simple function to take a number and build its Roman numeral equivalent. While I was mulling it over this morning, I spontaneously thought of a method to validate and parse a Roman numeral and turn it back into Arabic numbers using regular expressions.&lt;/p&gt;&lt;a name='more'&gt;&lt;/a&gt;&lt;blockquote&gt;&lt;form action=# onSubmit="this.flip.value=translate(this.flip.value, this.strict.checked); return false"&gt;&lt;b&gt;Input a Roman numeral or an integer between 1 and 3999:&lt;/b&gt;&lt;br/&gt;&lt;input type=text name=flip /&gt;&lt;input type=submit value=Translate /&gt;&lt;input type=checkbox name=strict&gt; Strict&lt;/form&gt;&lt;/blockquote&gt;&lt;p class=section&gt;Step 1: Validating&lt;/p&gt;&lt;script&gt;var isInt  = /^\d+$/;var isRom  = /^[IVXLCDM]+$/i;var valid  = /^M{0,3}(D?C{0,4}|C[MD])(L?X{0,4}|X[CL])(V?I{0,4}|I[XV])$/;var strict = /^M{0,3}(D?C{0,3}|C[MD])(L?X{0,3}|X[CL])(V?I{0,3}|I[XV])$/;var word   = /(M|CM|D|CD|C|XC|L|XL|X|IX|V|IV|I)/g;var value  = {I:1,IV:4,V:5,IX:9,X:10,XL:40,L:50,XC:90,C:100,CD:400,D:500,CM:900,M:1000};function translate(input, isStrict) {  if (input == null || input == "") {    alert("Please enter a value.");    return input;  }  var output;  if (input.match(isRom)) output = getInt(input.toUpperCase(), isStrict);  else if (input.match(isInt)) output = getRom(input);  else alert(input + " is not a Roman numeral or integer value.");  return output == null ? input : output;}function getInt(input, isStrict) {  var matcher = isStrict ? strict : valid;  if (input.match(matcher) == null) {    alert(input + " is not a valid Roman numeral.");    return;  }  var s = 0;  var words = input.match(word);  for (var i = 0; i &lt; words.length; i++) {    s += value[words[i]];  }  return s;}function getRom(input) {  var num = parseInt(input, 10);  if (num &lt; 1 || num &gt; 3999) {    alert("Integer must be between 1 and 3999.");    return;  }  var s = "";  while (num &gt;= 1000) { num -= 1000; s +=  "M"; }  if    (num &gt;=  900) { num -=  900; s += "CM"; }  if    (num &gt;=  500) { num -=  500; s +=  "D"; }  if    (num &gt;=  400) { num -=  400; s += "CD"; }  while (num &gt;=  100) { num -=  100; s +=  "C"; }  if    (num &gt;=   90) { num -=   90; s += "XC"; }  if    (num &gt;=   50) { num -=   50; s +=  "L"; }  if    (num &gt;=   40) { num -=   40; s += "XL"; }  while (num &gt;=   10) { num -=   10; s +=  "X"; }  if    (num &gt;=    9) { num -=    9; s += "IX"; }  if    (num &gt;=    5) { num -=    5; s +=  "V"; }  if    (num &gt;=    4) { num -=    4; s += "IV"; }  while (num &gt;=    1) { num -=    1; s +=  "I"; }  return s;}&lt;/script&gt;&lt;p&gt;What contitutes a "valid" Roman numeral is ambiguous. For example, referring to 4 as IV instead of IIII has more of a spotty history than I imagined. The Wikipedia article on roman numerals has an &lt;a href="http://en.wikipedia.org/wiki/Roman_numerals#IIII_and_IV"&gt;interesting section on this&lt;/a&gt;, discussing the use of IIII on clock faces, and MDCCCC on 20th century buildings instead of MCM, and some instances of using VV to represent 10. I decided not to support VV, and to add a flag that sets whether to reject IIII, XXXX, and CCCC.&lt;/p&gt;&lt;p&gt;Assuming IIII, VIIII, IV, and IX are all valid, here are all the possible ones place Roman numerals:&lt;/p&gt;&lt;pre&gt;&lt;br /&gt;I&lt;br /&gt;II&lt;br /&gt;III&lt;br /&gt;IV or IIII&lt;br /&gt;V&lt;br /&gt;VI&lt;br /&gt;VII&lt;br /&gt;VIII&lt;br /&gt;IX or VIIII&lt;br /&gt;&lt;/pre&gt;&lt;p&gt;These can be divided into two classes: IX and IV, and everything else. If the numeral contains IX or IV, you're done with the ones place. If it doesn't contain either of those, the numeral can have a V or not followed by 0 to 4 I characters (0 to 3 if we're being strict). Expressing that concept in regular expression speak is surprisingly simple, and can be built from three basic rules. I'll describe what each rule means, for those not familiar with regex.&lt;/p&gt;&lt;pre&gt;&lt;br /&gt;I[XV]  - This means 'I' followed by either 'X' or 'V'.&lt;br /&gt;V?     - 0 or 1 V characters&lt;br /&gt;I{0,4} - 0 to 4 I characters&lt;br /&gt;&lt;/pre&gt;&lt;p&gt;Now we just need to put the optional V before the optional I's, and specify either-or to the choice types by using the pipe character, making the rule:&lt;/p&gt;&lt;pre&gt;&lt;br /&gt;V?I{0,4}|I[XV]&lt;br /&gt;&lt;/pre&gt;&lt;p&gt;Next, the same rule needs to be applied to the tens and hundreds places, and each rule wrapped in parentheses to keep the piped choices from interfering with each other:&lt;/p&gt;&lt;pre&gt;&lt;br /&gt;(D?C{0,4}|C[MD])(L?X{0,4}|X[CL])(V?I{0,4}|I[XV])&lt;br /&gt;&lt;/pre&gt;&lt;p&gt;Lastly, the thousands, and binding to the start and end of the string. The beginning of the string is marked with the carat symbol, the end with the dollar sign. There can be 0 to 3 thousands at the beginning of the string, followed by the regex above, followed by the end of the string. This makes the complete regex:&lt;/p&gt;&lt;pre&gt;&lt;br /&gt;^M{0,3}(D?C{0,4}|C[MD])(L?X{0,4}|X[CL])(V?I{0,4}|I[XV])$&lt;br /&gt;&lt;/pre&gt;&lt;p class=section&gt;Step 2: Evaluating&lt;/p&gt;&lt;p&gt;Once we're sure the string is valid, we need to break it down into tokens and add up their values. Fortunately, every letter has a set value, and only the exceptions for subtraction notation (IX, IV, etc.) need to be worried about. Regular Expressions can help us out again, as the piped list of choices uses a first match rule. We just need a list of choices that finds the subtraction notation options before it finds the individual letters:&lt;/p&gt;&lt;pre&gt;&lt;br /&gt;M|CM|D|CD|C|XC|L|XL|X|IX|V|IV|I&lt;br /&gt;&lt;/pre&gt;&lt;p&gt;Note that IX and IV are found before just plain 'I'. Next, assign each list option a value, and write a function to iterate over all the matches and add up the values:&lt;/p&gt;&lt;pre&gt;&lt;br /&gt;var word   = /(M|CM|D|CD|C|XC|L|XL|X|IX|V|IV|I)/g;&lt;br /&gt;var value  = {I:1,IV:4,V:5,IX:9,X:10,XL:40,L:50,XC:90,C:100,CD:400,D:500,CM:900,M:1000};&lt;br /&gt;&lt;br /&gt;function getInt(input) {&lt;br /&gt;  var s = 0;&lt;br /&gt;  var words = input.match(word);&lt;br /&gt;  for (var i = 0; i &amp;lt; words.length; i++) {&lt;br /&gt;    s += value[words[i]];&lt;br /&gt;  }&lt;br /&gt;  return s;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;p class=section&gt;Putting it all together&lt;/p&gt;&lt;p&gt;Using those basic concepts, and borrowing from my previous code that built a Roman numeral based on an Arabic number, I wrote a smallish function that can take either an Arabic number or Roman numeral, and convert it to the other.&lt;/p&gt;The source code:&lt;pre&gt;&lt;br /&gt;var isInt  = /^\d+$/;&lt;br /&gt;var isRom  = /^[IVXLCDM]+$/i;&lt;br /&gt;var valid  = /^M{0,3}(D?C{0,4}|C[MD])(L?X{0,4}|X[CL])(V?I{0,4}|I[XV])$/;&lt;br /&gt;var strict = /^M{0,3}(D?C{0,3}|C[MD])(L?X{0,3}|X[CL])(V?I{0,3}|I[XV])$/;&lt;br /&gt;var word   = /(M|CM|D|CD|C|XC|L|XL|X|IX|V|IV|I)/g;&lt;br /&gt;var value  = {I:1,IV:4,V:5,IX:9,X:10,XL:40,L:50,XC:90,C:100,CD:400,D:500,CM:900,M:1000};&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;function translate(input, isStrict) {&lt;br /&gt;  if (input == null || input == "") {&lt;br /&gt;    alert("Please enter a value.");&lt;br /&gt;    return input;&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  var output;&lt;br /&gt;  if (input.match(isRom)) output = getInt(input.toUpperCase(), isStrict);&lt;br /&gt;  else if (input.match(isInt)) output = getRom(input);&lt;br /&gt;  else alert(input + " is not a Roman numeral or integer value.");&lt;br /&gt;&lt;br /&gt;  return output == null ? input : output;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;function getInt(input, isStrict) {&lt;br /&gt;  var matcher = isStrict ? strict : valid;&lt;br /&gt;  if (input.match(matcher) == null) {&lt;br /&gt;    alert(input + " is not a valid Roman numeral.");&lt;br /&gt;    return;&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  var s = 0;&lt;br /&gt;  var words = input.match(word);&lt;br /&gt;  for (var i = 0; i &amp;lt; words.length; i++) {&lt;br /&gt;    s += value[words[i]];&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  return s;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;function getRom(input) {&lt;br /&gt;  var num = parseInt(input, 10);&lt;br /&gt;&lt;br /&gt;  if (num &amp;lt; 1 || num &amp;gt; 3999) {&lt;br /&gt;    alert("Integer must be between 1 and 3999.");&lt;br /&gt;    return;&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  var s = "";&lt;br /&gt;  while (num &amp;gt;= 1000) { num -= 1000; s +=  "M"; }&lt;br /&gt;  if    (num &amp;gt;=  900) { num -=  900; s += "CM"; }&lt;br /&gt;  if    (num &amp;gt;=  500) { num -=  500; s +=  "D"; }&lt;br /&gt;  if    (num &amp;gt;=  400) { num -=  400; s += "CD"; }&lt;br /&gt;  while (num &amp;gt;=  100) { num -=  100; s +=  "C"; }&lt;br /&gt;  if    (num &amp;gt;=   90) { num -=   90; s += "XC"; }&lt;br /&gt;  if    (num &amp;gt;=   50) { num -=   50; s +=  "L"; }&lt;br /&gt;  if    (num &amp;gt;=   40) { num -=   40; s += "XL"; }&lt;br /&gt;  while (num &amp;gt;=   10) { num -=   10; s +=  "X"; }&lt;br /&gt;  if    (num &amp;gt;=    9) { num -=    9; s += "IX"; }&lt;br /&gt;  if    (num &amp;gt;=    5) { num -=    5; s +=  "V"; }&lt;br /&gt;  if    (num &amp;gt;=    4) { num -=    4; s += "IV"; }&lt;br /&gt;  while (num &amp;gt;=    1) { num -=    1; s +=  "I"; }&lt;br /&gt;  return s;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11885757-5930073477840759254?l=cautery.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://cautery.blogspot.com/feeds/5930073477840759254/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://cautery.blogspot.com/2010/11/roman-numerals-redux.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11885757/posts/default/5930073477840759254'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11885757/posts/default/5930073477840759254'/><link rel='alternate' type='text/html' href='http://cautery.blogspot.com/2010/11/roman-numerals-redux.html' title='Roman numerals redux'/><author><name>Curtis Autery</name><uri>https://profiles.google.com/107677530285177731535</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh4.googleusercontent.com/--J3C_khtLqQ/AAAAAAAAAAI/AAAAAAAADT4/e2Qa5ncfAMk/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11885757.post-5439384927112673223</id><published>2010-11-15T19:55:00.000-05:00</published><updated>2010-11-15T19:55:06.570-05:00</updated><title type='text'>The crazy geometry of basketball courts</title><content type='html'>I saw an interesting question on &lt;a href="http://blog.mrmeyer.com/?p=8450&amp;utm_source=feedburner&amp;utm_medium=feed&amp;utm_campaign=Feed:+dydan1+(dy/dan+posts+%2B+lessons)"&gt;dy/dan&lt;/a&gt; today about posing basketball-related math questions to students. One of the reader suggestions was to talk open-endedly about the geometry of the court itself, to which Dan suggested writing a program that would draw a court given two mouse clicks to denote the baseline. That sounded right up my alley, so I wrote one.&lt;br /&gt;&lt;br /&gt;Before starting to code, I had to learn about the measurements of a basketball court, and I was a little surprised by what I found. First and foremost, every league has its own sizes for every piece of the court, most differing by a foot or less. The biggest discrepancy is with the length of the court, which is 10 feet less for high school than for NCAA and above, or about 2 running steps. Naturally the FIBA league had measurements rounded to meters where all the US leagues are rounded to feet.&lt;br /&gt;&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;A couple other things struck me as odd. First, the backboard is 4 feet from the baseline - inside the court. I always assumed it was flush with the baseline. A second bad assumption of mine was that the three-point line was a conic section, like a parabola or hyperbola. It's not. It's a semi-circle at the top, and straight lines at the bottom, like a horseshoe. The rules imply this shape, but only refer to the three-point line as an arc.&lt;br /&gt;&lt;br /&gt;The last organized basketball game I went to was with my daughter and my mentee, and we watched the Ohio State women's team trounce an unranked opponent from the comfort of the AEP suite at the Schottenstein center. As a nod to both of them, and to the AEP mentoring program which scored me the free tickets, all the measurements below are regulation NCAA women's court measurements. And I can assure you those players are capable of shooting from further out than their ridiculously truncated three-point line. And playing with the same ball their male counterparts use: two ounces heavier and one inch more in circumference. I honestly didn't start writing this as a feminist entry; the discrepancies just upset me after I thought about them for a while. Anyway, back to the code.&lt;br /&gt;&lt;br /&gt;Java applet to draw a court:&lt;br /&gt;&lt;applet style="background:#ccc;padding:5px;" archive="https://sites.google.com/site/ceauterytest/Home/drawCourt.jar" code="cea/demos/drawCourt/DrawCourt.class" width=600 height=600&gt;&lt;br /&gt;&lt;/applet&gt;&lt;br /&gt;Source code:&lt;br /&gt;&lt;pre&gt;package cea.demos.drawCourt;&lt;br /&gt;import java.applet.Applet;&lt;br /&gt;import java.awt.BasicStroke;&lt;br /&gt;import java.awt.Color;&lt;br /&gt;import java.awt.Graphics;&lt;br /&gt;import java.awt.Graphics2D;&lt;br /&gt;import java.awt.Point;&lt;br /&gt;import java.awt.RenderingHints;&lt;br /&gt;import java.awt.Shape;&lt;br /&gt;import java.awt.Stroke;&lt;br /&gt;import java.awt.event.MouseAdapter;&lt;br /&gt;import java.awt.event.MouseEvent;&lt;br /&gt;import java.awt.geom.AffineTransform;&lt;br /&gt;import java.awt.geom.Arc2D;&lt;br /&gt;import java.awt.geom.Ellipse2D;&lt;br /&gt;import java.awt.geom.GeneralPath;&lt;br /&gt;import java.awt.geom.Rectangle2D;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;public class DrawCourt extends Applet {&lt;br /&gt;  private static final long serialVersionUID = 1L;&lt;br /&gt;  // Paths describing the court lines, and the bottom-most free-throw separators, which are filled in and 1' foot thick&lt;br /&gt;  GeneralPath court, fill;&lt;br /&gt;  Shape courtTransformed, fillTransformed; // The same shapes after an affineTransform is applied to them&lt;br /&gt;  Stroke stroke; // How thick of a line to draw the court with. Most lines are 2" thick (relative to court size)&lt;br /&gt;  // Applet has three draw/input modes: Getting the upper-left point, the upper-right point, and drawing the court&lt;br /&gt;  enum Mode {GET_UL_POINT, GET_UR_POINT, PAINT_COURT}&lt;br /&gt;  Mode mode;&lt;br /&gt;  Point upperLeft;&lt;br /&gt;&lt;br /&gt;  public void init() {&lt;br /&gt;    mode = Mode.GET_UL_POINT;&lt;br /&gt;    addMouseListener(new GetClick());&lt;br /&gt;&lt;br /&gt;    // Initialize court shape object&lt;br /&gt;    court = new GeneralPath();&lt;br /&gt;    fill = new GeneralPath();&lt;br /&gt;&lt;br /&gt;    // Build the court shape. Comments describe what each line is, and the rectangle, ellipse, and arc&lt;br /&gt;    // objects describe the bounding rectangles of the lines&lt;br /&gt;    &lt;br /&gt;    // Out-of-bounds rectangle, 50' by 94'&lt;br /&gt;    court.append(new Rectangle2D.Double(0, 0, 50, 94), false);&lt;br /&gt;    &lt;br /&gt;    // Coaching box markers, 28' from baseline, three feet long, on both sides of court &lt;br /&gt;    court.append(new Rectangle2D.Double( 0, 28, 3, 0), false);&lt;br /&gt;    court.append(new Rectangle2D.Double(47, 28, 3, 0), false);&lt;br /&gt;    court.append(new Rectangle2D.Double( 0, 66, 3, 0), false);&lt;br /&gt;    court.append(new Rectangle2D.Double(47, 66, 3, 0), false);&lt;br /&gt;    &lt;br /&gt;    // Center-court line, 47' from baseline (94 / 2 = 47), all the way across court (50')&lt;br /&gt;    court.append(new Rectangle2D.Double(0, 47, 50, 0), false);&lt;br /&gt;    &lt;br /&gt;    // Restraining circle and center circle, 2' and 6' radii from middle of center-court line&lt;br /&gt;    court.append(new Ellipse2D.Double(23, 45,  4,  4), false);&lt;br /&gt;    court.append(new Ellipse2D.Double(19, 41, 12, 12), false);&lt;br /&gt;    &lt;br /&gt;    // Back-boards and baskets&lt;br /&gt;    // Back-boards are 4' inside court from baseline, centered, and 6' wide&lt;br /&gt;    court.append(new Rectangle2D.Double(22,  4, 6, 0), false);&lt;br /&gt;    court.append(new Rectangle2D.Double(22, 90, 6, 0), false);&lt;br /&gt;    // Basket are 18" circles mounted 6" from backboard&lt;br /&gt;    court.append(new Ellipse2D.Double(24.25,  4.5, 1.5, 1.5), false);&lt;br /&gt;    court.append(new Ellipse2D.Double(24.25, 88.0, 1.5, 1.5), false);&lt;br /&gt;    &lt;br /&gt;    // NCAA women's 3-point lines are 19'9" from basket's center, until 5'3" from court's side,&lt;br /&gt;    // then straight down to baseline. The rules describing court layouts are ambiguous about this,&lt;br /&gt;    // but this appears to be how they're laid out in reality.&lt;br /&gt;    //  top&lt;br /&gt;    court.append(new Arc2D.Double(5.25, -14.5, 39.5, 39.5, 0, -180, Arc2D.OPEN), false);&lt;br /&gt;    court.append(new Rectangle2D.Double( 5.25, 0, 0, 5.25), false);&lt;br /&gt;    court.append(new Rectangle2D.Double(44.75, 0, 0, 5.25), false);&lt;br /&gt;    // bottom&lt;br /&gt;    court.append(new Arc2D.Double(5.25, 69, 39.5, 39.5, 0, 180, Arc2D.OPEN), false);&lt;br /&gt;    court.append(new Rectangle2D.Double( 5.25, 88.75, 0, 5.25), false);&lt;br /&gt;    court.append(new Rectangle2D.Double(44.75, 88.75, 0, 5.25), false);&lt;br /&gt;    &lt;br /&gt;    // "The Paint", 12' by 19' rectangle, centered and touching baseline&lt;br /&gt;    court.append(new Rectangle2D.Double(19,  0, 12, 19), false);&lt;br /&gt;    court.append(new Rectangle2D.Double(19, 75, 12, 19), false);&lt;br /&gt;    &lt;br /&gt;    // Free-throw circles, the top of The Paint (the free-throw line) is the circle's diameter&lt;br /&gt;    court.append(new Ellipse2D.Double(19, 13, 12, 12), false);&lt;br /&gt;    court.append(new Ellipse2D.Double(19, 69, 12, 12), false);&lt;br /&gt;    &lt;br /&gt;    // Free-throw bottom separators (need to be filled in), are 5' from baseline, 8" wide, 1' long&lt;br /&gt;    // and adjacent to The Paint&lt;br /&gt;    fill.append(new Rectangle2D.Double(19.0d - 2.0d/3, 7, 2.0d/3, 1), false);&lt;br /&gt;    fill.append(new Rectangle2D.Double(31, 7, 2.0d/3, 1), false);&lt;br /&gt;    fill.append(new Rectangle2D.Double(19.0d - 2.0d/3, 86, 2.0d/3, 1), false);&lt;br /&gt;    fill.append(new Rectangle2D.Double(31, 86, 2.0d/3, 1), false);&lt;br /&gt;    &lt;br /&gt;    // Other free-throw separators, 3' between edges, also 8" wide and adjacent to The Paint,&lt;br /&gt;    // but only 2" long&lt;br /&gt;    // top-left&lt;br /&gt;    court.append(new Rectangle2D.Double(19.0d - 2.0d/3, 11, 2.0d/3, 0), false);&lt;br /&gt;    court.append(new Rectangle2D.Double(19.0d - 2.0d/3, 14.0d + 1.0d/6, 2.0d/3, 0), false);&lt;br /&gt;    court.append(new Rectangle2D.Double(19.0d - 2.0d/3, 17.0d + 1.0d/3, 2.0d/3, 0), false);&lt;br /&gt;    // top-right&lt;br /&gt;    court.append(new Rectangle2D.Double(31, 11, 2.0d/3, 0), false);&lt;br /&gt;    court.append(new Rectangle2D.Double(31, 14.0d + 1.0d/6, 2.0d/3, 0), false);&lt;br /&gt;    court.append(new Rectangle2D.Double(31, 17.0d + 1.0d/3, 2.0d/3, 0), false);&lt;br /&gt;    // bottom-left&lt;br /&gt;    court.append(new Rectangle2D.Double(19.0d - 2.0d/3, 83, 2.0d/3, 0), false);&lt;br /&gt;    court.append(new Rectangle2D.Double(19.0d - 2.0d/3, 80.0d - 1.0d/6, 2.0d/3, 0), false);&lt;br /&gt;    court.append(new Rectangle2D.Double(19.0d - 2.0d/3, 77.0d - 1.0d/3, 2.0d/3, 0), false);&lt;br /&gt;    // bottom-right&lt;br /&gt;    court.append(new Rectangle2D.Double(31, 83, 2.0d/3, 0), false);&lt;br /&gt;    court.append(new Rectangle2D.Double(31, 80.0d - 1.0d/6, 2.0d/3, 0), false);&lt;br /&gt;    court.append(new Rectangle2D.Double(31, 77.0d - 1.0d/3, 2.0d/3, 0), false);&lt;br /&gt;  }&lt;br /&gt;  &lt;br /&gt;  // This method creates transformed shapes of the court shape archetypes.&lt;br /&gt;  public void scaleCourt(Point left, Point right) {&lt;br /&gt;    double radians;&lt;br /&gt;    if (left.x == right.x) radians = left.y &amp;lt; right.y ? Math.PI/2 : -Math.PI/2; // Avoid divide by 0 error&lt;br /&gt;    else radians = Math.atan((float)(right.y - left.y)/(right.x - left.x));&lt;br /&gt;    if (right.x &amp;lt; left.x)radians += Math.PI; // Fix angle if in quadrants 2 or 3&lt;br /&gt;&lt;br /&gt;    /* "factor" bears some explanation. The default size of the court shape is 1 pixel per foot. Two points&lt;br /&gt;     * on the screen are clicked, representing corners of the baseline: 50' across. So, the factor to scale the shape&lt;br /&gt;     * will be the real distance between the corner points divided by the default size of 50.&lt;br /&gt;     */&lt;br /&gt;    float factor = (float) (left.distance(right)/50);&lt;br /&gt;    stroke = new BasicStroke(factor * 2.0f / 12); // Default stroke = 2 inches relative to court size&lt;br /&gt;    AffineTransform tx = new AffineTransform();&lt;br /&gt;    tx.setToScale(factor, factor);&lt;br /&gt;    tx.rotate(radians);&lt;br /&gt;    courtTransformed = tx.createTransformedShape(court);&lt;br /&gt;    fillTransformed = tx.createTransformedShape(fill);&lt;br /&gt;  }&lt;br /&gt;  &lt;br /&gt;  public void paint(Graphics g1) {&lt;br /&gt;    Graphics2D g = (Graphics2D)g1;&lt;br /&gt;    g.setColor(Color.BLACK);&lt;br /&gt;    switch(mode) {&lt;br /&gt;    case GET_UL_POINT:&lt;br /&gt;      g.drawString("Click where the upper-left corner of the court will be.", 50, 50);&lt;br /&gt;      break;&lt;br /&gt;    case GET_UR_POINT:&lt;br /&gt;      g.fillOval(upperLeft.x - 4, upperLeft.y - 4, 8, 8);&lt;br /&gt;      g.drawString("Now click where the upper-right corner should be", 50, 50);&lt;br /&gt;      break;&lt;br /&gt;    case PAINT_COURT:&lt;br /&gt;      g.drawString("Click again to start over", 50, 50);&lt;br /&gt;      g.setStroke(stroke);&lt;br /&gt;      g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);&lt;br /&gt;      g.translate(upperLeft.x, upperLeft.y);&lt;br /&gt;      g.draw(courtTransformed);&lt;br /&gt;      g.fill(fillTransformed);&lt;br /&gt;    }&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  class GetClick extends MouseAdapter {&lt;br /&gt;    @Override&lt;br /&gt;    public void mouseClicked(MouseEvent e) {&lt;br /&gt;      switch (mode) {&lt;br /&gt;      case GET_UL_POINT:&lt;br /&gt;        upperLeft = e.getPoint();&lt;br /&gt;        mode = Mode.GET_UR_POINT;&lt;br /&gt;        repaint();&lt;br /&gt;        break;&lt;br /&gt;      case GET_UR_POINT:&lt;br /&gt;        mode = Mode.PAINT_COURT;&lt;br /&gt;        scaleCourt(upperLeft, e.getPoint());&lt;br /&gt;        repaint();&lt;br /&gt;        break;&lt;br /&gt;      case PAINT_COURT:&lt;br /&gt;        mode = Mode.GET_UL_POINT;&lt;br /&gt;        repaint();&lt;br /&gt;      }&lt;br /&gt;    }&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11885757-5439384927112673223?l=cautery.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://cautery.blogspot.com/feeds/5439384927112673223/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://cautery.blogspot.com/2010/11/crazy-geometry-of-basketball-courts.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11885757/posts/default/5439384927112673223'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11885757/posts/default/5439384927112673223'/><link rel='alternate' type='text/html' href='http://cautery.blogspot.com/2010/11/crazy-geometry-of-basketball-courts.html' title='The crazy geometry of basketball courts'/><author><name>Curtis Autery</name><uri>https://profiles.google.com/107677530285177731535</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh4.googleusercontent.com/--J3C_khtLqQ/AAAAAAAAAAI/AAAAAAAADT4/e2Qa5ncfAMk/s512-c/photo.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11885757.post-1418854516356189852</id><published>2010-11-11T17:54:00.004-05:00</published><updated>2010-11-11T17:59:12.629-05:00</updated><title type='text'>Jury Duty</title><content type='html'>On Wednesday, when I was up walking around to limber up after reading in a chair for a few hours, I came across the bulletin board in the jury pool waiting room. The board had several local newspaper op-ed pieces on jury service that may as well have been written by a chatbot, randomly generating from a dictionary of truisms and catch-phrases. "Ten Reasons I'm Glad I Served. 1: To see the inner-workings of the law. 2: To meet new friends..." Hence this post, a gritty look at my experience being on jury duty. The management summary: my experience was interesting, but the process is flawed. Like Randy Pees used to say, "Our legal system is terrible. The only one worse than ours is everyone else's."&lt;br /&gt;&lt;br /&gt;About two weeks prior to my reading of the platitude-laden corkboard, I received a summons in the mail to report for jury duty. I tried to get out of it by replying with a letter claiming a child-care burden, as I pick up Scout from school on Tuesdays and Thursdays so that Liberty can attend later classes on those days. I got a phone call the next day telling me basically to suck it up and make other arrangements. Conveniently, this did not prove to be an actual problem: On Tuesday we were released before 5pm, and the courthouse was closed today for Veteran's Day.&lt;br /&gt;&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;On Monday, the summons instructed me to report at 8am, an hour before any trial could conceivably start. I spent most of that hour in a hot and crowded room waiting in line with a two page questionnaire asking me, among other things, whether I knew any police officers, if I had ever seen the inside of a courtroom before, or was involved in a lawsuit or related to anyone who had. My answers -- which were yes, yes, and yes -- would be handed directly to the lawyers of any case I would be assigned to later.&lt;br /&gt;&lt;br /&gt;The rest of the hour was spent in an auditorium watching a film showing people dressed in Middle Ages period garb performing a trial by ordeal (tossing someone in the water to test their guilt -- float = guilty, drown = innocent), to show us how far we have come in these enlightened days, followed by a question and answer session by a septuagenarian judge whose cheerfulness and flamboyant mannerisms would have put Leo Buscaglia to shame. Oddly, the judge claimed that the process of trial by jury began with the Magna Carta, ignoring the Athenian's use of jurors some 1800 years earlier. Perhaps the 500 jurors of Socrates rings a bell, judge? No?&lt;br /&gt;&lt;br /&gt;A more credible fact brought up in the lecture was that of the 400 people summoned for this week's service, less than 200 showed up, which is about average. I've come up with two explanations for this, neither of which bode well for my country. First, companies are not required to pay you while you're on jury duty, and the courts only pay you a nominal wage ($20/day) meant to cover parking and lunch. Second, people consider themselves consumers and not citizens, and have no sense of civic duty.&lt;br /&gt;&lt;br /&gt;"But wait," you may be saying, "didn't you yourself try to get out of jury duty? Isn't it hypocritical to claim other people aren't civic-minded?" To answer, here is the text of the letter I sent to the jury commission:&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;Request for Curtis Autery being excused from jury duty for child care reasons&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Dear sirs,&lt;br /&gt;&lt;br /&gt;My wife, Liberty, has late classes at OSU on Tuesdays and Thursdays.  These classes run until 5:18pm.  On these days, I pick up my daughter, Scout, from school around 4pm so that she can have dinner and get started on her homework.&lt;br /&gt;&lt;br /&gt;If I were to serve jury duty, Scout would be required to stay in after-school care on these days, which is only available until 6pm.  The time it would take Liberty to get to her car and then fight rush-hour traffic to get to Scout’s school would typically have her arriving after 6pm.  This would place a financial burden on us in paying fines each day this happened (which would be most Tuesdays and Thursdays), and the disruption of Scout’s well-being as she would have a late dinner and not get started on her homework until she was tired in the evening.&lt;br /&gt;&lt;br /&gt;I respectfully request to be excused from this jury duty assignment.  With that said, I welcome the opportunity to serve, and each semester at OSU brings my wife a different schedule, so this disruption may not apply should I get summoned again in the future.&lt;br /&gt;&lt;br /&gt;Thank you very much.&lt;br /&gt;&lt;br /&gt;Curtis Autery&lt;br /&gt;[address, phone]&lt;br /&gt;&lt;/blockquote&gt;&lt;br /&gt;The letter mentions that I would like another chance to serve if they excused me this time, and when told to quit whining and show up like a man, that's exactly what I did.&lt;br /&gt;&lt;br /&gt;The $20/day and employers not being required to pay is more interesting, though. If you have a working class job that doesn't pay for jury service, you're probably living paycheck to paycheck already, and can't afford the time off, and hence can't serve. Many criminal cases involve defendants with working class jobs. How then, can they get a jury of their peers?&lt;br /&gt;&lt;br /&gt;Or let's take the racial angle: If you live in an area with rich white people and poor black people, and a black man is on trial, what are the jurors likely to be? White, because they can either afford the time off, or their employers pay their normal salary during jury service. Are they likely to be sympathetic to the black man on trial? No. Biased against? Yes.&lt;br /&gt;&lt;br /&gt;I don't consider myself to be especially political or an activist, but the way jury service is run is a clear disservice to my fellow man, perhaps explaining why there are so many black men in prison, many of which are later found not to be guilty of their accused crimes through the help of The Innocence Project.&lt;br /&gt;&lt;br /&gt;On with the story.&lt;br /&gt;&lt;br /&gt;Day 1 continued with waiting for a trial to start, which happened roughly 30 minutes later, and I was one of the 24 people called up to be potential jurors. The case was Zimara Ball v. Jessica Stark, involving (from the pretrial statement) "an automobile/pedestrian accident which occurred on June 3, 2006, when then­10-year­old Zimara Ball darted onto Brown Road ... As Zimara Ball did so, she ran into the passenger side front quarter panel of a vehicle being driven by Defendant Jessica Stark."&lt;br /&gt;&lt;br /&gt;Per directions from the court, I refrained from looking up anything about the case until I was discharged from jury service. Oddly, and despite being a seasoned data miner, I can find no mention of this incident in the news when it happened, or coverage of the current trial. Jessica's employer at the time, Maronda homes, has painstakingly expunged any reference to her from its website. News site searches for both parties reveals nothing other than Zimara making the 8th grade honor roll this year -- congratulations! The only information I could find about the case was on the court website itself, which is kind enough to supply scanned copies of all the motions and filings. &lt;br /&gt;&lt;br /&gt;The courtroom had a handful of people in it before I and my fellow potential jurors came in. The plaintiff's mom (effectively the real plaintiff) and her legal staff, Jessica and her legal staff, a lawyer representing Maronda Homes, the judge, the court reporter, and a couple random people with no apparent function to serve were all standing and waiting for us to come in and find our seats. After we were sworn in, the judge began to explain voir dire, the process of weeding out bad jurors.&lt;br /&gt;&lt;br /&gt;This was a civil trial, meaning there are 8 jurors and two alternates, and only a 3/4 majority is needed to decide guilt. The judge, and then the lawyers, would use our questionnaires, and direct and indirect questioning to determine if anyone shouldn't be on the jury. The reasons one wouldn't be appropriate to be on the jury would be having a bias against "the system", personally knowing any of the parties, having passable knowledge of anything expert witnesses would testify about (I have mixed feelings about this one), or having a similar personal experience to what was being tried.&lt;br /&gt;&lt;br /&gt;So around the room the judge went, and around the room the lawyers went, and in about an hour we found that two men had children that had been murdered, a mom had severe anxiety about child safety, a narcissistic woman habitually receives traffic tickets and distrusts any police testimony, I myself have been hit by a car while bicycling, and just about everyone had been in court for some reason before. The parents of the murdered children, crying-towel mom, and the spazzy driver were all let go, but for whatever reason they kept me on until the second day.&lt;br /&gt;&lt;br /&gt;Here is where some distasteful things happen. Here is where lawyer jokes come from. In fact, here's one now: Q) How do you save a lawyer from drowning? A) Take your foot off his head.&lt;br /&gt;&lt;br /&gt;The judge painstakingly explains why not discussing the case or drawing any conclusions early is important. One of the reasons he gives is the following example of two jurors, let's call them George and Bob, talking about the case over lunch...&lt;br /&gt;&lt;br /&gt;"Gee, Bob, I really didn't find that last witness convincing. I think he's lying." George has just committed to a point of view out loud, and for some people it's difficult to go against an opinion you've stated openly. If George now hears more evidence that supports the witness he thought was lying, he will be more hesitant to change his mind than if he had said nothing. Apparently saying "I may have been wrong before" is difficult for people. Or possibly nobody reads Emerson&lt;sub&gt;1&lt;/sub&gt; any more.&lt;br /&gt;&lt;br /&gt;On top of the admonition not to talk about the case, it was made clear that we weren't trying the case, or hearing any evidence in it yet. We were instead in a vague, nebulous discussion about issues that may come up during the trial and whether our personal biases would put objectivity in peril before any &lt;b&gt;real&lt;/b&gt; evidence was heard.&lt;br /&gt;&lt;br /&gt;The judge was specifically avoiding giving out information about the case with his questions, and avoiding questions leading us to profess a belief. The lawyers, however, had no such qualms. Objections flew, and the judge stopped the questioning on his own multiple times, as the lawyers jockeyed for position.&lt;br /&gt;&lt;br /&gt;The plaintiff's lawyer asked us all to commit, under oath, to the belief that everyone should always, under all circumstances, slow down when a child is anywhere near a road. This tells me two things: The defendant didn't slow down, and there is a reason that she didn't think she needed to. And the lawyer wanted me to say "oh yes, there can never be a reason not to instantaneously slow down when a kid is present." Having delivered pizza for two years, and being a father, I understand that kids have no peripheral vision, no understanding of physics or consequences, follow bouncing balls into the street like a moth to a flame, and can generally be assumed to be at least unpredictable, if not batshit crazy. None of that implies, though, that braking should be automatic whenever there are any kids anywhere. The question implied that there is some ambiguity about whether slowing was appropriate, and some of us already began to commit to a point of view we might want to reconsider later, exactly the type of thing the judge warned us not to do.&lt;br /&gt;&lt;br /&gt;He next asked us if we had ever hit anything in our car that we didn't see, and all who answered in the affirmative were later excused from the case. So the defendant probably claims to have not seen the girl darting into the road, and later the plaintiff's lawyer will claim this not to be possible.&lt;br /&gt;&lt;br /&gt;The defendant's lawyer had her turn then, and went on a rant about today's children looking older than they are, trying to get everyone to admit that they were bad judges of kids' ages, just like her client. And then the question of how old a kid should be before she should be considered competent to cross a street unassisted came up. We had already heard at this point that the accident happened when the girl was 10, a nice round number, and at least half of the remaining jurors put 10 as the age at which kids shouldn't be darting out into traffic any more. So the defendant may have seen the kid or may not, and if she did, then she thought the kid was older, but either way, believes that once you're 10 you shouldn't be doing anything stupid like that anyway. So there are lots of possible defenses in play, and the lawyer was getting a read on which one would work best.&lt;br /&gt;&lt;br /&gt;So that pretty much laid the whole trial out, right there in the voir dire proceeding, with at least two thirds of us committing to some solid point of view that the lawyers will be trying to leverage throughout the trial. This was all the exact opposite of what was supposed to be happening, and all the court officials played this theater of pretending nothing mistrial-worthy had just happened.&lt;br /&gt;&lt;br /&gt;I was sickened, and once the day's proceedings were over, was very glad to go home.&lt;br /&gt;&lt;br /&gt;Day 2 involved a lot of waiting, and none of the preceding day's ethical problems. I parked my car at my work's garage, hoofed it to the courthouse, put on my juror's badge to make me an un-person -- to warn off lawyers and case parties from engaging in conversation with me (strangers don't tend to seek out my conversation, so I wasn't very concerned about this) -- signed in at the jury pool waiting room, and headed up to the sixth floor where our courtroom was. We assembled at 9:30, entered the courtroom, and were asked a single question from the judge: "Did you hear the news story last night about the traffic accident?" We were then told to leave and re-assemble at 10. At 10:15, they finally called us back in, where they announced that the jury had been selected. Ten of our names were read. My name wasn't one of them.&lt;br /&gt;&lt;br /&gt;I spent the rest of my time in the insanely hot jury pool waiting room, reading "The Terror" by Dan Simmons. By day 3 I had finished the first 100 pages, and couldn't sit in the uncomfortable chairs any longer, so got up and meandered over to find the newspaper editorials, yellowed with age, espousing the awesomeness of jury duty, being a citizen, and America itself. I like America just fine, and the benefits of being a citizen, and I don't shirk the duties that come with that, but the duties aren't as rose-colored as puff-piece articles would lead one to believe.&lt;br /&gt;&lt;br /&gt;1 - From Ralph Waldo Emerson's "Self-Reliance": Speak what you think now in hard words, and to-morrow speak what to-morrow thinks in hard words again, though it contradict every thing you said to-day.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11885757-1418854516356189852?l=cautery.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://cautery.blogspot.com/feeds/1418854516356189852/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://cautery.blogspot.com/2010/11/jury-duty.html#comment-form' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11885757/posts/default/1418854516356189852'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11885757/posts/default/1418854516356189852'/><link rel='alternate' type='text/html' href='http://cautery.blogspot.com/2010/11/jury-duty.html' title='Jury Duty'/><author><name>Curtis Autery</name><uri>https://profiles.google.com/107677530285177731535</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh4.googleusercontent.com/--J3C_khtLqQ/AAAAAAAAAAI/AAAAAAAADT4/e2Qa5ncfAMk/s512-c/photo.jpg'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11885757.post-1188827460075014484</id><published>2010-10-24T20:55:00.001-04:00</published><updated>2010-10-24T20:56:37.365-04:00</updated><title type='text'>Awesome Ted talks</title><content type='html'>(plus some David Byrne worship, because he rocks)&lt;br /&gt;(and Seth Godin at GEL, who also rocks)&lt;br /&gt;&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;David Byrne sings Nothing but Flowers&lt;br /&gt;&lt;object width="446" height="326"&gt;&lt;param name="movie" value="http://video.ted.com/assets/player/swf/EmbedPlayer.swf"&gt;&lt;/param&gt;&lt;param name="allowFullScreen" value="true" /&gt;&lt;param name="allowScriptAccess" value="always"/&gt;&lt;param name="wmode" value="transparent"&gt;&lt;/param&gt;&lt;param name="bgColor" value="#ffffff"&gt;&lt;/param&gt;&lt;param name="flashvars" value="vu=http://video.ted.com/talks/dynamic/DavidByrne_FLOWERS_2010-medium.flv&amp;su=http://images.ted.com/images/ted/tedindex/embed-posters/DavidByrne-Flowers-2010.embed_thumbnail.jpg&amp;vw=432&amp;vh=240&amp;ap=0&amp;ti=988&amp;introDuration=15330&amp;adDuration=4000&amp;postAdDuration=830&amp;adKeys=talk=david_byrne_sings_nothing_but_flowers;year=2010;theme=spectacular_performance;theme=a_greener_future;theme=inspired_by_nature;theme=the_creative_spark;theme=a_taste_of_ted2010;event=TED2010;&amp;preAdTag=tconf.ted/embed;tile=1;sz=512x288;" /&gt;&lt;embed src="http://video.ted.com/assets/player/swf/EmbedPlayer.swf" pluginspace="http://www.macromedia.com/go/getflashplayer" type="application/x-shockwave-flash" wmode="transparent" bgColor="#ffffff" width="446" height="326" allowFullScreen="true" allowScriptAccess="always" flashvars="vu=http://video.ted.com/talks/dynamic/DavidByrne_FLOWERS_2010-medium.flv&amp;su=http://images.ted.com/images/ted/tedindex/embed-posters/DavidByrne-Flowers-2010.embed_thumbnail.jpg&amp;vw=432&amp;vh=240&amp;ap=0&amp;ti=988&amp;introDuration=15330&amp;adDuration=4000&amp;postAdDuration=830&amp;adKeys=talk=david_byrne_sings_nothing_but_flowers;year=2010;theme=spectacular_performance;theme=a_greener_future;theme=inspired_by_nature;theme=the_creative_spark;theme=a_taste_of_ted2010;event=TED2010;"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;br /&gt;&lt;br /&gt;How architecture helped music evolve&lt;br /&gt;&lt;object width="446" height="326"&gt;&lt;param name="movie" value="http://video.ted.com/assets/player/swf/EmbedPlayer.swf"&gt;&lt;/param&gt;&lt;param name="allowFullScreen" value="true" /&gt;&lt;param name="allowScriptAccess" value="always"/&gt;&lt;param name="wmode" value="transparent"&gt;&lt;/param&gt;&lt;param name="bgColor" value="#ffffff"&gt;&lt;/param&gt;&lt;param name="flashvars" value="vu=http://video.ted.com/talks/dynamic/DavidByrne_2010-medium.flv&amp;su=http://images.ted.com/images/ted/tedindex/embed-posters/DavidByrne-2010.embed_thumbnail.jpg&amp;vw=432&amp;vh=240&amp;ap=0&amp;ti=883&amp;introDuration=15330&amp;adDuration=4000&amp;postAdDuration=830&amp;adKeys=talk=david_byrne_how_architecture_helped_music_evolve;year=2010;theme=unconventional_explanations;theme=art_unusual;theme=the_creative_spark;theme=a_taste_of_ted2010;theme=new_on_ted_com;event=TED2010;&amp;preAdTag=tconf.ted/embed;tile=1;sz=512x288;" /&gt;&lt;embed src="http://video.ted.com/assets/player/swf/EmbedPlayer.swf" pluginspace="http://www.macromedia.com/go/getflashplayer" type="application/x-shockwave-flash" wmode="transparent" bgColor="#ffffff" width="446" height="326" allowFullScreen="true" allowScriptAccess="always" flashvars="vu=http://video.ted.com/talks/dynamic/DavidByrne_2010-medium.flv&amp;su=http://images.ted.com/images/ted/tedindex/embed-posters/DavidByrne-2010.embed_thumbnail.jpg&amp;vw=432&amp;vh=240&amp;ap=0&amp;ti=883&amp;introDuration=15330&amp;adDuration=4000&amp;postAdDuration=830&amp;adKeys=talk=david_byrne_how_architecture_helped_music_evolve;year=2010;theme=unconventional_explanations;theme=art_unusual;theme=the_creative_spark;theme=a_taste_of_ted2010;theme=new_on_ted_com;event=TED2010;"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;br /&gt;&lt;br /&gt;How schools kill creativity&lt;br /&gt;&lt;object width="334" height="326"&gt;&lt;param name="movie" value="http://video.ted.com/assets/player/swf/EmbedPlayer.swf"&gt;&lt;/param&gt;&lt;param name="allowFullScreen" value="true" /&gt;&lt;param name="allowScriptAccess" value="always"/&gt;&lt;param name="wmode" value="transparent"&gt;&lt;/param&gt;&lt;param name="bgColor" value="#ffffff"&gt;&lt;/param&gt;&lt;param name="flashvars" value="vu=http://video.ted.com/talks/dynamic/SirKenRobinson_2006-medium.flv&amp;su=http://images.ted.com/images/ted/tedindex/embed-posters/SirKenRobinson-2006.embed_thumbnail.jpg&amp;vw=320&amp;vh=240&amp;ap=0&amp;ti=66&amp;introDuration=15330&amp;adDuration=4000&amp;postAdDuration=830&amp;adKeys=talk=ken_robinson_says_schools_kill_creativity;year=2006;theme=top_10_tedtalks;theme=master_storytellers;theme=the_creative_spark;theme=how_the_mind_works;theme=bold_predictions_stern_warnings;theme=how_we_learn;event=TED2006;&amp;preAdTag=tconf.ted/embed;tile=1;sz=512x288;" /&gt;&lt;embed src="http://video.ted.com/assets/player/swf/EmbedPlayer.swf" pluginspace="http://www.macromedia.com/go/getflashplayer" type="application/x-shockwave-flash" wmode="transparent" bgColor="#ffffff" width="334" height="326" allowFullScreen="true" allowScriptAccess="always" flashvars="vu=http://video.ted.com/talks/dynamic/SirKenRobinson_2006-medium.flv&amp;su=http://images.ted.com/images/ted/tedindex/embed-posters/SirKenRobinson-2006.embed_thumbnail.jpg&amp;vw=320&amp;vh=240&amp;ap=0&amp;ti=66&amp;introDuration=15330&amp;adDuration=4000&amp;postAdDuration=830&amp;adKeys=talk=ken_robinson_says_schools_kill_creativity;year=2006;theme=top_10_tedtalks;theme=master_storytellers;theme=the_creative_spark;theme=how_the_mind_works;theme=bold_predictions_stern_warnings;theme=how_we_learn;event=TED2006;"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;br /&gt;&lt;br /&gt;Malcolm Gladwell on spaghetti sauce&lt;br /&gt;&lt;object width="446" height="326"&gt;&lt;param name="movie" value="http://video.ted.com/assets/player/swf/EmbedPlayer.swf"&gt;&lt;/param&gt;&lt;param name="allowFullScreen" value="true" /&gt;&lt;param name="allowScriptAccess" value="always"/&gt;&lt;param name="wmode" value="transparent"&gt;&lt;/param&gt;&lt;param name="bgColor" value="#ffffff"&gt;&lt;/param&gt;&lt;param name="flashvars" value="vu=http://video.ted.com/talks/dynamic/MalcolmGladwell_2004-medium.flv&amp;su=http://images.ted.com/images/ted/tedindex/embed-posters/MalcolmGladwell-2004.embed_thumbnail.jpg&amp;vw=432&amp;vh=240&amp;ap=0&amp;ti=20&amp;introDuration=15330&amp;adDuration=4000&amp;postAdDuration=830&amp;adKeys=talk=malcolm_gladwell_on_spaghetti_sauce;year=2004;theme=not_business_as_usual;theme=tales_of_invention;theme=unconventional_explanations;theme=what_makes_us_happy;event=TED2004;&amp;preAdTag=tconf.ted/embed;tile=1;sz=512x288;" /&gt;&lt;embed src="http://video.ted.com/assets/player/swf/EmbedPlayer.swf" pluginspace="http://www.macromedia.com/go/getflashplayer" type="application/x-shockwave-flash" wmode="transparent" bgColor="#ffffff" width="446" height="326" allowFullScreen="true" allowScriptAccess="always" flashvars="vu=http://video.ted.com/talks/dynamic/MalcolmGladwell_2004-medium.flv&amp;su=http://images.ted.com/images/ted/tedindex/embed-posters/MalcolmGladwell-2004.embed_thumbnail.jpg&amp;vw=432&amp;vh=240&amp;ap=0&amp;ti=20&amp;introDuration=15330&amp;adDuration=4000&amp;postAdDuration=830&amp;adKeys=talk=malcolm_gladwell_on_spaghetti_sauce;year=2004;theme=not_business_as_usual;theme=tales_of_invention;theme=unconventional_explanations;theme=what_makes_us_happy;event=TED2004;"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;br /&gt;&lt;br /&gt;The child-driven education. If you only pick one to watch, pick this one.&lt;br /&gt;&lt;object width="446" height="326"&gt;&lt;param name="movie" value="http://video.ted.com/assets/player/swf/EmbedPlayer.swf"&gt;&lt;/param&gt;&lt;param name="allowFullScreen" value="true" /&gt;&lt;param name="allowScriptAccess" value="always"/&gt;&lt;param name="wmode" value="transparent"&gt;&lt;/param&gt;&lt;param name="bgColor" value="#ffffff"&gt;&lt;/param&gt;&lt;param name="flashvars" value="vu=http://video.ted.com/talks/dynamic/SugataMitra_2010G-medium.flv&amp;su=http://images.ted.com/images/ted/tedindex/embed-posters/SugataMitra-2010G.embed_thumbnail.jpg&amp;vw=432&amp;vh=240&amp;ap=0&amp;ti=949&amp;introDuration=15330&amp;adDuration=4000&amp;postAdDuration=830&amp;adKeys=talk=sugata_mitra_the_child_driven_education;year=2010;theme=new_on_ted_com;theme=how_the_mind_works;theme=unconventional_explanations;theme=rethinking_poverty;theme=a_taste_of_tedglobal_2010;event=TEDGlobal+2010;&amp;preAdTag=tconf.ted/embed;tile=1;sz=512x288;" /&gt;&lt;embed src="http://video.ted.com/assets/player/swf/EmbedPlayer.swf" pluginspace="http://www.macromedia.com/go/getflashplayer" type="application/x-shockwave-flash" wmode="transparent" bgColor="#ffffff" width="446" height="326" allowFullScreen="true" allowScriptAccess="always" flashvars="vu=http://video.ted.com/talks/dynamic/SugataMitra_2010G-medium.flv&amp;su=http://images.ted.com/images/ted/tedindex/embed-posters/SugataMitra-2010G.embed_thumbnail.jpg&amp;vw=432&amp;vh=240&amp;ap=0&amp;ti=949&amp;introDuration=15330&amp;adDuration=4000&amp;postAdDuration=830&amp;adKeys=talk=sugata_mitra_the_child_driven_education;year=2010;theme=new_on_ted_com;theme=how_the_mind_works;theme=unconventional_explanations;theme=rethinking_poverty;theme=a_taste_of_tedglobal_2010;event=TEDGlobal+2010;"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;br /&gt;&lt;br /&gt;Economic reality check&lt;br /&gt;&lt;object width="446" height="326"&gt;&lt;param name="movie" value="http://video.ted.com/assets/player/swf/EmbedPlayer.swf"&gt;&lt;/param&gt;&lt;param name="allowFullScreen" value="true" /&gt;&lt;param name="allowScriptAccess" value="always"/&gt;&lt;param name="wmode" value="transparent"&gt;&lt;/param&gt;&lt;param name="bgColor" value="#ffffff"&gt;&lt;/param&gt;&lt;param name="flashvars" value="vu=http://video.ted.com/talks/dynamic/TimJackson_2010G-medium.flv&amp;su=http://images.ted.com/images/ted/tedindex/embed-posters/TimJackson-2010G.embed_thumbnail.jpg&amp;vw=432&amp;vh=240&amp;ap=0&amp;ti=972&amp;introDuration=15330&amp;adDuration=4000&amp;postAdDuration=830&amp;adKeys=talk=tim_jackson_s_economic_reality_check;year=2010;theme=bold_predictions_stern_warnings;theme=new_on_ted_com;theme=rethinking_poverty;theme=a_greener_future;theme=a_taste_of_tedglobal_2010;theme=unconventional_explanations;theme=not_business_as_usual;event=TEDGlobal+2010;&amp;preAdTag=tconf.ted/embed;tile=1;sz=512x288;" /&gt;&lt;embed src="http://video.ted.com/assets/player/swf/EmbedPlayer.swf" pluginspace="http://www.macromedia.com/go/getflashplayer" type="application/x-shockwave-flash" wmode="transparent" bgColor="#ffffff" width="446" height="326" allowFullScreen="true" allowScriptAccess="always" flashvars="vu=http://video.ted.com/talks/dynamic/TimJackson_2010G-medium.flv&amp;su=http://images.ted.com/images/ted/tedindex/embed-posters/TimJackson-2010G.embed_thumbnail.jpg&amp;vw=432&amp;vh=240&amp;ap=0&amp;ti=972&amp;introDuration=15330&amp;adDuration=4000&amp;postAdDuration=830&amp;adKeys=talk=tim_jackson_s_economic_reality_check;year=2010;theme=bold_predictions_stern_warnings;theme=new_on_ted_com;theme=rethinking_poverty;theme=a_greener_future;theme=a_taste_of_tedglobal_2010;theme=unconventional_explanations;theme=not_business_as_usual;event=TEDGlobal+2010;"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;br /&gt;&lt;br /&gt;The good news of the decade. Wait out the odd opening for a kickin' conclusion.&lt;br /&gt;&lt;object width="334" height="326"&gt;&lt;param name="movie" value="http://video.ted.com/assets/player/swf/EmbedPlayer.swf"&gt;&lt;/param&gt;&lt;param name="allowFullScreen" value="true" /&gt;&lt;param name="allowScriptAccess" value="always"/&gt;&lt;param name="wmode" value="transparent"&gt;&lt;/param&gt;&lt;param name="bgColor" value="#ffffff"&gt;&lt;/param&gt;&lt;param name="flashvars" value="vu=http://video.ted.com/talks/dynamic/HansRosling_2010X-medium.flv&amp;su=http://images.ted.com/images/ted/tedindex/embed-posters/HansRosling-2010X.embed_thumbnail.jpg&amp;vw=320&amp;vh=240&amp;ap=0&amp;ti=974&amp;introDuration=15330&amp;adDuration=4000&amp;postAdDuration=830&amp;adKeys=talk=hans_rosling_the_good_news_of_the_decade;year=2010;theme=medicine_without_borders;theme=rethinking_poverty;theme=a_greener_future;theme=unconventional_explanations;theme=a_taste_of_tedx;theme=bold_predictions_stern_warnings;theme=new_on_ted_com;event=TEDxChange;&amp;preAdTag=tconf.ted/embed;tile=1;sz=512x288;" /&gt;&lt;embed src="http://video.ted.com/assets/player/swf/EmbedPlayer.swf" pluginspace="http://www.macromedia.com/go/getflashplayer" type="application/x-shockwave-flash" wmode="transparent" bgColor="#ffffff" width="334" height="326" allowFullScreen="true" allowScriptAccess="always" flashvars="vu=http://video.ted.com/talks/dynamic/HansRosling_2010X-medium.flv&amp;su=http://images.ted.com/images/ted/tedindex/embed-posters/HansRosling-2010X.embed_thumbnail.jpg&amp;vw=320&amp;vh=240&amp;ap=0&amp;ti=974&amp;introDuration=15330&amp;adDuration=4000&amp;postAdDuration=830&amp;adKeys=talk=hans_rosling_the_good_news_of_the_decade;year=2010;theme=medicine_without_borders;theme=rethinking_poverty;theme=a_greener_future;theme=unconventional_explanations;theme=a_taste_of_tedx;theme=bold_predictions_stern_warnings;theme=new_on_ted_com;event=TEDxChange;"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;br /&gt;&lt;br /&gt;This is broken!&lt;br /&gt;&lt;iframe src="http://player.vimeo.com/video/4246943" width="400" height="300" frameborder="0"&gt;&lt;/iframe&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11885757-1188827460075014484?l=cautery.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://cautery.blogspot.com/feeds/1188827460075014484/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://cautery.blogspot.com/2010/10/awesome-ted-talks.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11885757/posts/default/1188827460075014484'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11885757/posts/default/1188827460075014484'/><link rel='alternate' type='text/html' href='http://cautery.blogspot.com/2010/10/awesome-ted-talks.html' title='Awesome Ted talks'/><author><name>Curtis Autery</name><uri>https://profiles.google.com/107677530285177731535</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh4.googleusercontent.com/--J3C_khtLqQ/AAAAAAAAAAI/AAAAAAAADT4/e2Qa5ncfAMk/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11885757.post-2402613362530408438</id><published>2010-10-23T10:20:00.004-04:00</published><updated>2010-10-23T10:23:40.722-04:00</updated><title type='text'>Fun music videos, mostly 80s</title><content type='html'>Some videos that are cool.  No reason.&lt;br /&gt;&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;&lt;object width="640" height="505"&gt;&lt;param name="movie" value="http://www.youtube.com/v/I1wg1DNHbNU?fs=1&amp;amp;hl=en_US&amp;amp;rel=0"&gt;&lt;/param&gt;&lt;param name="allowFullScreen" value="true"&gt;&lt;/param&gt;&lt;param name="allowscriptaccess" value="always"&gt;&lt;/param&gt;&lt;embed src="http://www.youtube.com/v/I1wg1DNHbNU?fs=1&amp;amp;hl=en_US&amp;amp;rel=0" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="640" height="505"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;br /&gt;&lt;br /&gt;&lt;object width="640" height="385"&gt;&lt;param name="movie" value="http://www.youtube.com/v/mxfjSnMN88U?fs=1&amp;amp;hl=en_US&amp;amp;rel=0"&gt;&lt;/param&gt;&lt;param name="allowFullScreen" value="true"&gt;&lt;/param&gt;&lt;param name="allowscriptaccess" value="always"&gt;&lt;/param&gt;&lt;embed src="http://www.youtube.com/v/mxfjSnMN88U?fs=1&amp;amp;hl=en_US&amp;amp;rel=0" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="640" height="385"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;br /&gt;&lt;br /&gt;&lt;object width="640" height="505"&gt;&lt;param name="movie" value="http://www.youtube.com/v/7movKfyTBII?fs=1&amp;amp;hl=en_US&amp;amp;rel=0"&gt;&lt;/param&gt;&lt;param name="allowFullScreen" value="true"&gt;&lt;/param&gt;&lt;param name="allowscriptaccess" value="always"&gt;&lt;/param&gt;&lt;embed src="http://www.youtube.com/v/7movKfyTBII?fs=1&amp;amp;hl=en_US&amp;amp;rel=0" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="640" height="505"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;br /&gt;&lt;br /&gt;&lt;object width="640" height="385"&gt;&lt;param name="movie" value="http://www.youtube.com/v/_EXxMlIExpo?fs=1&amp;amp;hl=en_US&amp;amp;rel=0"&gt;&lt;/param&gt;&lt;param name="allowFullScreen" value="true"&gt;&lt;/param&gt;&lt;param name="allowscriptaccess" value="always"&gt;&lt;/param&gt;&lt;embed src="http://www.youtube.com/v/_EXxMlIExpo?fs=1&amp;amp;hl=en_US&amp;amp;rel=0" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="640" height="385"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;br /&gt;&lt;br /&gt;&lt;object width="640" height="505"&gt;&lt;param name="movie" value="http://www.youtube.com/v/Be0H_AI1zys?fs=1&amp;amp;hl=en_US&amp;amp;rel=0"&gt;&lt;/param&gt;&lt;param name="allowFullScreen" value="true"&gt;&lt;/param&gt;&lt;param name="allowscriptaccess" value="always"&gt;&lt;/param&gt;&lt;embed src="http://www.youtube.com/v/Be0H_AI1zys?fs=1&amp;amp;hl=en_US&amp;amp;rel=0" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="640" height="505"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;br /&gt;&lt;br /&gt;&lt;object width="640" height="505"&gt;&lt;param name="movie" value="http://www.youtube.com/v/PIb6AZdTr-A?fs=1&amp;amp;hl=en_US&amp;amp;rel=0"&gt;&lt;/param&gt;&lt;param name="allowFullScreen" value="true"&gt;&lt;/param&gt;&lt;param name="allowscriptaccess" value="always"&gt;&lt;/param&gt;&lt;embed src="http://www.youtube.com/v/PIb6AZdTr-A?fs=1&amp;amp;hl=en_US&amp;amp;rel=0" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="640" height="505"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;br /&gt;&lt;br /&gt;&lt;object width="640" height="505"&gt;&lt;param name="movie" value="http://www.youtube.com/v/G1dlWmrRstc?fs=1&amp;amp;hl=en_US&amp;amp;rel=0"&gt;&lt;/param&gt;&lt;param name="allowFullScreen" value="true"&gt;&lt;/param&gt;&lt;param name="allowscriptaccess" value="always"&gt;&lt;/param&gt;&lt;embed src="http://www.youtube.com/v/G1dlWmrRstc?fs=1&amp;amp;hl=en_US&amp;amp;rel=0" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="640" height="505"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11885757-2402613362530408438?l=cautery.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://cautery.blogspot.com/feeds/2402613362530408438/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://cautery.blogspot.com/2010/10/fun-videos-mostly-80s.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11885757/posts/default/2402613362530408438'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11885757/posts/default/2402613362530408438'/><link rel='alternate' type='text/html' href='http://cautery.blogspot.com/2010/10/fun-videos-mostly-80s.html' title='Fun music videos, mostly 80s'/><author><name>Curtis Autery</name><uri>https://profiles.google.com/107677530285177731535</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh4.googleusercontent.com/--J3C_khtLqQ/AAAAAAAAAAI/AAAAAAAADT4/e2Qa5ncfAMk/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11885757.post-757497807665613101</id><published>2010-10-09T06:34:00.011-04:00</published><updated>2010-10-09T06:47:46.995-04:00</updated><title type='text'>JavaScript Roman numeral calculator</title><content type='html'>Yesterday I saw a homework question on a coding forum I frequent regarding turning Arabic numbers into Roman numerals. Homework questions are a strange breed. Most of the time the posters are blatantly cheating, some times they are up front that the question is for an assignment and they just want a push in the right direction. Responses range from helpful, following the Wikipedia "assume good faith" mantra, to cynical, to pretty damned rude. Forum admins enjoy flexing their dictatorial muscles in these cases, posting warnings, flames, and deleting posts.&lt;br /&gt;&lt;br /&gt;My mantra in these cases is simple: If the question is interesting, I try to answer it. If the poster isn't cut out for a programming career, getting my answer isn't going to change that. If he is, he's going to be asking his peers a lot of questions over his career, as no one is born with an innate gift at coding; it's a learned skill like anything else. The young "super hacker" is generally the geek equivalent of a bully - not as skilled/tough as he pretends.&lt;br /&gt;&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;I'm using "he" and "his" a lot here, which is deliberate. There are women coders, sure, but for whatever reason they don't frequent the programming forums. I can only assume they tackle problems more intelligently, like asking a trusted source they know personally, or using things like Google.&lt;br /&gt;&lt;br /&gt;End sidebar.&lt;br /&gt;&lt;br /&gt;Anyway, I was as familiar as most people with how Roman numerals worked, namely numbers less than 40 made sense, then they get strange with the Ls, Cs, Ds, and Ms. So, I did a little research to fill in my knowledge gap. As it turns out, the concept is pretty straightforward, and revolves around powers of 10. For each power of 10, there are 4 cases: 1-3, 4, 5-8, and 9. Observe:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&amp;nbsp; 1 = I     4 = IV   5 = V      9 = IX&lt;br /&gt;  2 = II             6 = VI  &lt;br /&gt;  3 = III            7 = VII &lt;br /&gt;                     8 = VIII&lt;br /&gt;           &lt;br /&gt; 10 = X    40 = XL  50 = L     90 = XC&lt;br /&gt; 20 = XX            60 = LX  &lt;br /&gt; 30 = XXX           70 = LXX &lt;br /&gt;                    80 = LXXX&lt;br /&gt;          &lt;br /&gt;100 = C   400 = CD 500 = D    900 = CM         &lt;br /&gt;200 = CC           600 = DC  &lt;br /&gt;300 = CCC          700 = DCC &lt;br /&gt;                   800 = DCCC&lt;/pre&gt;&lt;br /&gt;Not so daunting, after all. These can be pieced together to build any number under 1000. For numbers between 1000 and 3999, just prepend with one M for every thousand. After that it gets weird with overscores, where a line over a letter means its value is multiplied by 1000. There seems to be little support for fonts with overscores, so I stuck with numbers under 4000.&lt;br /&gt;&lt;br /&gt;I tackled conversion without using any fancy coding constructs, instead just walking an input number down to 0 while building up the Roman numeral string:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;var s = "";&lt;br /&gt;while (num &amp;gt;= 1000) { num -= 1000; s +=  "M"; }&lt;br /&gt;if    (num &amp;gt;=  900) { num -=  900; s += "CM"; }&lt;br /&gt;if    (num &amp;gt;=  500) { num -=  500; s +=  "D"; }&lt;br /&gt;if    (num &amp;gt;=  400) { num -=  400; s += "CD"; }&lt;br /&gt;while (num &amp;gt;=  100) { num -=  100; s +=  "C"; }&lt;br /&gt;if    (num &amp;gt;=   90) { num -=   90; s += "XC"; }&lt;br /&gt;if    (num &amp;gt;=   50) { num -=   50; s +=  "L"; }&lt;br /&gt;if    (num &amp;gt;=   40) { num -=   40; s += "XL"; }&lt;br /&gt;while (num &amp;gt;=   10) { num -=   10; s +=  "X"; }&lt;br /&gt;if    (num &amp;gt;=    9) { num -=    9; s += "IX"; }&lt;br /&gt;if    (num &amp;gt;=    5) { num -=    5; s +=  "V"; }&lt;br /&gt;if    (num &amp;gt;=    4) { num -=    4; s += "IV"; }&lt;br /&gt;while (num &amp;gt;=    1) { num -=    1; s +=  "I"; }&lt;br /&gt;return s;&lt;/pre&gt;&lt;br /&gt;Try it out:&lt;script&gt;function getRomanNumeral(num) {  if(num &lt;= 0 || num &gt; 3999) {    alert("Number must be between 1 and 3999");    return;  }  var s = "";  while (num &gt;= 1000) { num -= 1000; s +=  "M"; }  if    (num &gt;=  900) { num -=  900; s += "CM"; }  if    (num &gt;=  500) { num -=  500; s +=  "D"; }  if    (num &gt;=  400) { num -=  400; s += "CD"; }  while (num &gt;=  100) { num -=  100; s +=  "C"; }  if    (num &gt;=   90) { num -=   90; s += "XC"; }  if    (num &gt;=   50) { num -=   50; s +=  "L"; }  if    (num &gt;=   40) { num -=   40; s += "XL"; }  while (num &gt;=   10) { num -=   10; s +=  "X"; }  if    (num &gt;=    9) { num -=    9; s += "IX"; }  if    (num &gt;=    5) { num -=    5; s +=  "V"; }  if    (num &gt;=    4) { num -=    4; s += "IV"; }  while (num &gt;=    1) { num -=    1; s +=  "I"; }  return s;}function getSingle() {  var retval = getRomanNumeral(document.getElementById("romanIn").value);  document.getElementById("romanOut").innerHTML = retval;}&lt;/script&gt;&lt;br /&gt;&lt;style&gt;td { padding-right=10px; }&lt;/style&gt;&lt;br /&gt;&lt;table&gt;&lt;tr&gt;&lt;td&gt;Enter a number between 1 and 3999: &lt;/td&gt;&lt;td&gt;&lt;input type=text size=15 id=romanIn&gt;&lt;/td&gt;&lt;td&gt;&lt;input type=button onclick=getSingle() value="Convert"&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td valign=top style=text-align:right;&gt;Roman numeral:&lt;/td&gt;&lt;td&gt;&lt;span id=romanOut&gt;&lt;/span&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;br /&gt;&lt;br /&gt;Also, there aren't any good lists of Roman numeral sequences on the Internet. They either stop at 100 and explain continuation rules, or are space-delimited with no associated Arabic numbers, or use the HTML "ordered list" trick, where you can bullet-point list items with Roman numerals that, sadly, can't be copied to your clipboard.&lt;br /&gt;&lt;br /&gt;To correct that oversight, here is a simple, copyable, well-formatted list of Roman numerals between 1 and 3999:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&amp;nbsp;  1 = I&lt;br /&gt;   2 = II&lt;br /&gt;   3 = III&lt;br /&gt;   4 = IV&lt;br /&gt;   5 = V&lt;br /&gt;   6 = VI&lt;br /&gt;   7 = VII&lt;br /&gt;   8 = VIII&lt;br /&gt;   9 = IX&lt;br /&gt;  10 = X&lt;br /&gt;  11 = XI&lt;br /&gt;  12 = XII&lt;br /&gt;  13 = XIII&lt;br /&gt;  14 = XIV&lt;br /&gt;  15 = XV&lt;br /&gt;  16 = XVI&lt;br /&gt;  17 = XVII&lt;br /&gt;  18 = XVIII&lt;br /&gt;  19 = XIX&lt;br /&gt;  20 = XX&lt;br /&gt;  21 = XXI&lt;br /&gt;  22 = XXII&lt;br /&gt;  23 = XXIII&lt;br /&gt;  24 = XXIV&lt;br /&gt;  25 = XXV&lt;br /&gt;  26 = XXVI&lt;br /&gt;  27 = XXVII&lt;br /&gt;  28 = XXVIII&lt;br /&gt;  29 = XXIX&lt;br /&gt;  30 = XXX&lt;br /&gt;  31 = XXXI&lt;br /&gt;  32 = XXXII&lt;br /&gt;  33 = XXXIII&lt;br /&gt;  34 = XXXIV&lt;br /&gt;  35 = XXXV&lt;br /&gt;  36 = XXXVI&lt;br /&gt;  37 = XXXVII&lt;br /&gt;  38 = XXXVIII&lt;br /&gt;  39 = XXXIX&lt;br /&gt;  40 = XL&lt;br /&gt;  41 = XLI&lt;br /&gt;  42 = XLII&lt;br /&gt;  43 = XLIII&lt;br /&gt;  44 = XLIV&lt;br /&gt;  45 = XLV&lt;br /&gt;  46 = XLVI&lt;br /&gt;  47 = XLVII&lt;br /&gt;  48 = XLVIII&lt;br /&gt;  49 = XLIX&lt;br /&gt;  50 = L&lt;br /&gt;  51 = LI&lt;br /&gt;  52 = LII&lt;br /&gt;  53 = LIII&lt;br /&gt;  54 = LIV&lt;br /&gt;  55 = LV&lt;br /&gt;  56 = LVI&lt;br /&gt;  57 = LVII&lt;br /&gt;  58 = LVIII&lt;br /&gt;  59 = LIX&lt;br /&gt;  60 = LX&lt;br /&gt;  61 = LXI&lt;br /&gt;  62 = LXII&lt;br /&gt;  63 = LXIII&lt;br /&gt;  64 = LXIV&lt;br /&gt;  65 = LXV&lt;br /&gt;  66 = LXVI&lt;br /&gt;  67 = LXVII&lt;br /&gt;  68 = LXVIII&lt;br /&gt;  69 = LXIX&lt;br /&gt;  70 = LXX&lt;br /&gt;  71 = LXXI&lt;br /&gt;  72 = LXXII&lt;br /&gt;  73 = LXXIII&lt;br /&gt;  74 = LXXIV&lt;br /&gt;  75 = LXXV&lt;br /&gt;  76 = LXXVI&lt;br /&gt;  77 = LXXVII&lt;br /&gt;  78 = LXXVIII&lt;br /&gt;  79 = LXXIX&lt;br /&gt;  80 = LXXX&lt;br /&gt;  81 = LXXXI&lt;br /&gt;  82 = LXXXII&lt;br /&gt;  83 = LXXXIII&lt;br /&gt;  84 = LXXXIV&lt;br /&gt;  85 = LXXXV&lt;br /&gt;  86 = LXXXVI&lt;br /&gt;  87 = LXXXVII&lt;br /&gt;  88 = LXXXVIII&lt;br /&gt;  89 = LXXXIX&lt;br /&gt;  90 = XC&lt;br /&gt;  91 = XCI&lt;br /&gt;  92 = XCII&lt;br /&gt;  93 = XCIII&lt;br /&gt;  94 = XCIV&lt;br /&gt;  95 = XCV&lt;br /&gt;  96 = XCVI&lt;br /&gt;  97 = XCVII&lt;br /&gt;  98 = XCVIII&lt;br /&gt;  99 = XCIX&lt;br /&gt; 100 = C&lt;br /&gt; 101 = CI&lt;br /&gt; 102 = CII&lt;br /&gt; 103 = CIII&lt;br /&gt; 104 = CIV&lt;br /&gt; 105 = CV&lt;br /&gt; 106 = CVI&lt;br /&gt; 107 = CVII&lt;br /&gt; 108 = CVIII&lt;br /&gt; 109 = CIX&lt;br /&gt; 110 = CX&lt;br /&gt; 111 = CXI&lt;br /&gt; 112 = CXII&lt;br /&gt; 113 = CXIII&lt;br /&gt; 114 = CXIV&lt;br /&gt; 115 = CXV&lt;br /&gt; 116 = CXVI&lt;br /&gt; 117 = CXVII&lt;br /&gt; 118 = CXVIII&lt;br /&gt; 119 = CXIX&lt;br /&gt; 120 = CXX&lt;br /&gt; 121 = CXXI&lt;br /&gt; 122 = CXXII&lt;br /&gt; 123 = CXXIII&lt;br /&gt; 124 = CXXIV&lt;br /&gt; 125 = CXXV&lt;br /&gt; 126 = CXXVI&lt;br /&gt; 127 = CXXVII&lt;br /&gt; 128 = CXXVIII&lt;br /&gt; 129 = CXXIX&lt;br /&gt; 130 = CXXX&lt;br /&gt; 131 = CXXXI&lt;br /&gt; 132 = CXXXII&lt;br /&gt; 133 = CXXXIII&lt;br /&gt; 134 = CXXXIV&lt;br /&gt; 135 = CXXXV&lt;br /&gt; 136 = CXXXVI&lt;br /&gt; 137 = CXXXVII&lt;br /&gt; 138 = CXXXVIII&lt;br /&gt; 139 = CXXXIX&lt;br /&gt; 140 = CXL&lt;br /&gt; 141 = CXLI&lt;br /&gt; 142 = CXLII&lt;br /&gt; 143 = CXLIII&lt;br /&gt; 144 = CXLIV&lt;br /&gt; 145 = CXLV&lt;br /&gt; 146 = CXLVI&lt;br /&gt; 147 = CXLVII&lt;br /&gt; 148 = CXLVIII&lt;br /&gt; 149 = CXLIX&lt;br /&gt; 150 = CL&lt;br /&gt; 151 = CLI&lt;br /&gt; 152 = CLII&lt;br /&gt; 153 = CLIII&lt;br /&gt; 154 = CLIV&lt;br /&gt; 155 = CLV&lt;br /&gt; 156 = CLVI&lt;br /&gt; 157 = CLVII&lt;br /&gt; 158 = CLVIII&lt;br /&gt; 159 = CLIX&lt;br /&gt; 160 = CLX&lt;br /&gt; 161 = CLXI&lt;br /&gt; 162 = CLXII&lt;br /&gt; 163 = CLXIII&lt;br /&gt; 164 = CLXIV&lt;br /&gt; 165 = CLXV&lt;br /&gt; 166 = CLXVI&lt;br /&gt; 167 = CLXVII&lt;br /&gt; 168 = CLXVIII&lt;br /&gt; 169 = CLXIX&lt;br /&gt; 170 = CLXX&lt;br /&gt; 171 = CLXXI&lt;br /&gt; 172 = CLXXII&lt;br /&gt; 173 = CLXXIII&lt;br /&gt; 174 = CLXXIV&lt;br /&gt; 175 = CLXXV&lt;br /&gt; 176 = CLXXVI&lt;br /&gt; 177 = CLXXVII&lt;br /&gt; 178 = CLXXVIII&lt;br /&gt; 179 = CLXXIX&lt;br /&gt; 180 = CLXXX&lt;br /&gt; 181 = CLXXXI&lt;br /&gt; 182 = CLXXXII&lt;br /&gt; 183 = CLXXXIII&lt;br /&gt; 184 = CLXXXIV&lt;br /&gt; 185 = CLXXXV&lt;br /&gt; 186 = CLXXXVI&lt;br /&gt; 187 = CLXXXVII&lt;br /&gt; 188 = CLXXXVIII&lt;br /&gt; 189 = CLXXXIX&lt;br /&gt; 190 = CXC&lt;br /&gt; 191 = CXCI&lt;br /&gt; 192 = CXCII&lt;br /&gt; 193 = CXCIII&lt;br /&gt; 194 = CXCIV&lt;br /&gt; 195 = CXCV&lt;br /&gt; 196 = CXCVI&lt;br /&gt; 197 = CXCVII&lt;br /&gt; 198 = CXCVIII&lt;br /&gt; 199 = CXCIX&lt;br /&gt; 200 = CC&lt;br /&gt; 201 = CCI&lt;br /&gt; 202 = CCII&lt;br /&gt; 203 = CCIII&lt;br /&gt; 204 = CCIV&lt;br /&gt; 205 = CCV&lt;br /&gt; 206 = CCVI&lt;br /&gt; 207 = CCVII&lt;br /&gt; 208 = CCVIII&lt;br /&gt; 209 = CCIX&lt;br /&gt; 210 = CCX&lt;br /&gt; 211 = CCXI&lt;br /&gt; 212 = CCXII&lt;br /&gt; 213 = CCXIII&lt;br /&gt; 214 = CCXIV&lt;br /&gt; 215 = CCXV&lt;br /&gt; 216 = CCXVI&lt;br /&gt; 217 = CCXVII&lt;br /&gt; 218 = CCXVIII&lt;br /&gt; 219 = CCXIX&lt;br /&gt; 220 = CCXX&lt;br /&gt; 221 = CCXXI&lt;br /&gt; 222 = CCXXII&lt;br /&gt; 223 = CCXXIII&lt;br /&gt; 224 = CCXXIV&lt;br /&gt; 225 = CCXXV&lt;br /&gt; 226 = CCXXVI&lt;br /&gt; 227 = CCXXVII&lt;br /&gt; 228 = CCXXVIII&lt;br /&gt; 229 = CCXXIX&lt;br /&gt; 230 = CCXXX&lt;br /&gt; 231 = CCXXXI&lt;br /&gt; 232 = CCXXXII&lt;br /&gt; 233 = CCXXXIII&lt;br /&gt; 234 = CCXXXIV&lt;br /&gt; 235 = CCXXXV&lt;br /&gt; 236 = CCXXXVI&lt;br /&gt; 237 = CCXXXVII&lt;br /&gt; 238 = CCXXXVIII&lt;br /&gt; 239 = CCXXXIX&lt;br /&gt; 240 = CCXL&lt;br /&gt; 241 = CCXLI&lt;br /&gt; 242 = CCXLII&lt;br /&gt; 243 = CCXLIII&lt;br /&gt; 244 = CCXLIV&lt;br /&gt; 245 = CCXLV&lt;br /&gt; 246 = CCXLVI&lt;br /&gt; 247 = CCXLVII&lt;br /&gt; 248 = CCXLVIII&lt;br /&gt; 249 = CCXLIX&lt;br /&gt; 250 = CCL&lt;br /&gt; 251 = CCLI&lt;br /&gt; 252 = CCLII&lt;br /&gt; 253 = CCLIII&lt;br /&gt; 254 = CCLIV&lt;br /&gt; 255 = CCLV&lt;br /&gt; 256 = CCLVI&lt;br /&gt; 257 = CCLVII&lt;br /&gt; 258 = CCLVIII&lt;br /&gt; 259 = CCLIX&lt;br /&gt; 260 = CCLX&lt;br /&gt; 261 = CCLXI&lt;br /&gt; 262 = CCLXII&lt;br /&gt; 263 = CCLXIII&lt;br /&gt; 264 = CCLXIV&lt;br /&gt; 265 = CCLXV&lt;br /&gt; 266 = CCLXVI&lt;br /&gt; 267 = CCLXVII&lt;br /&gt; 268 = CCLXVIII&lt;br /&gt; 269 = CCLXIX&lt;br /&gt; 270 = CCLXX&lt;br /&gt; 271 = CCLXXI&lt;br /&gt; 272 = CCLXXII&lt;br /&gt; 273 = CCLXXIII&lt;br /&gt; 274 = CCLXXIV&lt;br /&gt; 275 = CCLXXV&lt;br /&gt; 276 = CCLXXVI&lt;br /&gt; 277 = CCLXXVII&lt;br /&gt; 278 = CCLXXVIII&lt;br /&gt; 279 = CCLXXIX&lt;br /&gt; 280 = CCLXXX&lt;br /&gt; 281 = CCLXXXI&lt;br /&gt; 282 = CCLXXXII&lt;br /&gt; 283 = CCLXXXIII&lt;br /&gt; 284 = CCLXXXIV&lt;br /&gt; 285 = CCLXXXV&lt;br /&gt; 286 = CCLXXXVI&lt;br /&gt; 287 = CCLXXXVII&lt;br /&gt; 288 = CCLXXXVIII&lt;br /&gt; 289 = CCLXXXIX&lt;br /&gt; 290 = CCXC&lt;br /&gt; 291 = CCXCI&lt;br /&gt; 292 = CCXCII&lt;br /&gt; 293 = CCXCIII&lt;br /&gt; 294 = CCXCIV&lt;br /&gt; 295 = CCXCV&lt;br /&gt; 296 = CCXCVI&lt;br /&gt; 297 = CCXCVII&lt;br /&gt; 298 = CCXCVIII&lt;br /&gt; 299 = CCXCIX&lt;br /&gt; 300 = CCC&lt;br /&gt; 301 = CCCI&lt;br /&gt; 302 = CCCII&lt;br /&gt; 303 = CCCIII&lt;br /&gt; 304 = CCCIV&lt;br /&gt; 305 = CCCV&lt;br /&gt; 306 = CCCVI&lt;br /&gt; 307 = CCCVII&lt;br /&gt; 308 = CCCVIII&lt;br /&gt; 309 = CCCIX&lt;br /&gt; 310 = CCCX&lt;br /&gt; 311 = CCCXI&lt;br /&gt; 312 = CCCXII&lt;br /&gt; 313 = CCCXIII&lt;br /&gt; 314 = CCCXIV&lt;br /&gt; 315 = CCCXV&lt;br /&gt; 316 = CCCXVI&lt;br /&gt; 317 = CCCXVII&lt;br /&gt; 318 = CCCXVIII&lt;br /&gt; 319 = CCCXIX&lt;br /&gt; 320 = CCCXX&lt;br /&gt; 321 = CCCXXI&lt;br /&gt; 322 = CCCXXII&lt;br /&gt; 323 = CCCXXIII&lt;br /&gt; 324 = CCCXXIV&lt;br /&gt; 325 = CCCXXV&lt;br /&gt; 326 = CCCXXVI&lt;br /&gt; 327 = CCCXXVII&lt;br /&gt; 328 = CCCXXVIII&lt;br /&gt; 329 = CCCXXIX&lt;br /&gt; 330 = CCCXXX&lt;br /&gt; 331 = CCCXXXI&lt;br /&gt; 332 = CCCXXXII&lt;br /&gt; 333 = CCCXXXIII&lt;br /&gt; 334 = CCCXXXIV&lt;br /&gt; 335 = CCCXXXV&lt;br /&gt; 336 = CCCXXXVI&lt;br /&gt; 337 = CCCXXXVII&lt;br /&gt; 338 = CCCXXXVIII&lt;br /&gt; 339 = CCCXXXIX&lt;br /&gt; 340 = CCCXL&lt;br /&gt; 341 = CCCXLI&lt;br /&gt; 342 = CCCXLII&lt;br /&gt; 343 = CCCXLIII&lt;br /&gt; 344 = CCCXLIV&lt;br /&gt; 345 = CCCXLV&lt;br /&gt; 346 = CCCXLVI&lt;br /&gt; 347 = CCCXLVII&lt;br /&gt; 348 = CCCXLVIII&lt;br /&gt; 349 = CCCXLIX&lt;br /&gt; 350 = CCCL&lt;br /&gt; 351 = CCCLI&lt;br /&gt; 352 = CCCLII&lt;br /&gt; 353 = CCCLIII&lt;br /&gt; 354 = CCCLIV&lt;br /&gt; 355 = CCCLV&lt;br /&gt; 356 = CCCLVI&lt;br /&gt; 357 = CCCLVII&lt;br /&gt; 358 = CCCLVIII&lt;br /&gt; 359 = CCCLIX&lt;br /&gt; 360 = CCCLX&lt;br /&gt; 361 = CCCLXI&lt;br /&gt; 362 = CCCLXII&lt;br /&gt; 363 = CCCLXIII&lt;br /&gt; 364 = CCCLXIV&lt;br /&gt; 365 = CCCLXV&lt;br /&gt; 366 = CCCLXVI&lt;br /&gt; 367 = CCCLXVII&lt;br /&gt; 368 = CCCLXVIII&lt;br /&gt; 369 = CCCLXIX&lt;br /&gt; 370 = CCCLXX&lt;br /&gt; 371 = CCCLXXI&lt;br /&gt; 372 = CCCLXXII&lt;br /&gt; 373 = CCCLXXIII&lt;br /&gt; 374 = CCCLXXIV&lt;br /&gt; 375 = CCCLXXV&lt;br /&gt; 376 = CCCLXXVI&lt;br /&gt; 377 = CCCLXXVII&lt;br /&gt; 378 = CCCLXXVIII&lt;br /&gt; 379 = CCCLXXIX&lt;br /&gt; 380 = CCCLXXX&lt;br /&gt; 381 = CCCLXXXI&lt;br /&gt; 382 = CCCLXXXII&lt;br /&gt; 383 = CCCLXXXIII&lt;br /&gt; 384 = CCCLXXXIV&lt;br /&gt; 385 = CCCLXXXV&lt;br /&gt; 386 = CCCLXXXVI&lt;br /&gt; 387 = CCCLXXXVII&lt;br /&gt; 388 = CCCLXXXVIII&lt;br /&gt; 389 = CCCLXXXIX&lt;br /&gt; 390 = CCCXC&lt;br /&gt; 391 = CCCXCI&lt;br /&gt; 392 = CCCXCII&lt;br /&gt; 393 = CCCXCIII&lt;br /&gt; 394 = CCCXCIV&lt;br /&gt; 395 = CCCXCV&lt;br /&gt; 396 = CCCXCVI&lt;br /&gt; 397 = CCCXCVII&lt;br /&gt; 398 = CCCXCVIII&lt;br /&gt; 399 = CCCXCIX&lt;br /&gt; 400 = CD&lt;br /&gt; 401 = CDI&lt;br /&gt; 402 = CDII&lt;br /&gt; 403 = CDIII&lt;br /&gt; 404 = CDIV&lt;br /&gt; 405 = CDV&lt;br /&gt; 406 = CDVI&lt;br /&gt; 407 = CDVII&lt;br /&gt; 408 = CDVIII&lt;br /&gt; 409 = CDIX&lt;br /&gt; 410 = CDX&lt;br /&gt; 411 = CDXI&lt;br /&gt; 412 = CDXII&lt;br /&gt; 413 = CDXIII&lt;br /&gt; 414 = CDXIV&lt;br /&gt; 415 = CDXV&lt;br /&gt; 416 = CDXVI&lt;br /&gt; 417 = CDXVII&lt;br /&gt; 418 = CDXVIII&lt;br /&gt; 419 = CDXIX&lt;br /&gt; 420 = CDXX&lt;br /&gt; 421 = CDXXI&lt;br /&gt; 422 = CDXXII&lt;br /&gt; 423 = CDXXIII&lt;br /&gt; 424 = CDXXIV&lt;br /&gt; 425 = CDXXV&lt;br /&gt; 426 = CDXXVI&lt;br /&gt; 427 = CDXXVII&lt;br /&gt; 428 = CDXXVIII&lt;br /&gt; 429 = CDXXIX&lt;br /&gt; 430 = CDXXX&lt;br /&gt; 431 = CDXXXI&lt;br /&gt; 432 = CDXXXII&lt;br /&gt; 433 = CDXXXIII&lt;br /&gt; 434 = CDXXXIV&lt;br /&gt; 435 = CDXXXV&lt;br /&gt; 436 = CDXXXVI&lt;br /&gt; 437 = CDXXXVII&lt;br /&gt; 438 = CDXXXVIII&lt;br /&gt; 439 = CDXXXIX&lt;br /&gt; 440 = CDXL&lt;br /&gt; 441 = CDXLI&lt;br /&gt; 442 = CDXLII&lt;br /&gt; 443 = CDXLIII&lt;br /&gt; 444 = CDXLIV&lt;br /&gt; 445 = CDXLV&lt;br /&gt; 446 = CDXLVI&lt;br /&gt; 447 = CDXLVII&lt;br /&gt; 448 = CDXLVIII&lt;br /&gt; 449 = CDXLIX&lt;br /&gt; 450 = CDL&lt;br /&gt; 451 = CDLI&lt;br /&gt; 452 = CDLII&lt;br /&gt; 453 = CDLIII&lt;br /&gt; 454 = CDLIV&lt;br /&gt; 455 = CDLV&lt;br /&gt; 456 = CDLVI&lt;br /&gt; 457 = CDLVII&lt;br /&gt; 458 = CDLVIII&lt;br /&gt; 459 = CDLIX&lt;br /&gt; 460 = CDLX&lt;br /&gt; 461 = CDLXI&lt;br /&gt; 462 = CDLXII&lt;br /&gt; 463 = CDLXIII&lt;br /&gt; 464 = CDLXIV&lt;br /&gt; 465 = CDLXV&lt;br /&gt; 466 = CDLXVI&lt;br /&gt; 467 = CDLXVII&lt;br /&gt; 468 = CDLXVIII&lt;br /&gt; 469 = CDLXIX&lt;br /&gt; 470 = CDLXX&lt;br /&gt; 471 = CDLXXI&lt;br /&gt; 472 = CDLXXII&lt;br /&gt; 473 = CDLXXIII&lt;br /&gt; 474 = CDLXXIV&lt;br /&gt; 475 = CDLXXV&lt;br /&gt; 476 = CDLXXVI&lt;br /&gt; 477 = CDLXXVII&lt;br /&gt; 478 = CDLXXVIII&lt;br /&gt; 479 = CDLXXIX&lt;br /&gt; 480 = CDLXXX&lt;br /&gt; 481 = CDLXXXI&lt;br /&gt; 482 = CDLXXXII&lt;br /&gt; 483 = CDLXXXIII&lt;br /&gt; 484 = CDLXXXIV&lt;br /&gt; 485 = CDLXXXV&lt;br /&gt; 486 = CDLXXXVI&lt;br /&gt; 487 = CDLXXXVII&lt;br /&gt; 488 = CDLXXXVIII&lt;br /&gt; 489 = CDLXXXIX&lt;br /&gt; 490 = CDXC&lt;br /&gt; 491 = CDXCI&lt;br /&gt; 492 = CDXCII&lt;br /&gt; 493 = CDXCIII&lt;br /&gt; 494 = CDXCIV&lt;br /&gt; 495 = CDXCV&lt;br /&gt; 496 = CDXCVI&lt;br /&gt; 497 = CDXCVII&lt;br /&gt; 498 = CDXCVIII&lt;br /&gt; 499 = CDXCIX&lt;br /&gt; 500 = D&lt;br /&gt; 501 = DI&lt;br /&gt; 502 = DII&lt;br /&gt; 503 = DIII&lt;br /&gt; 504 = DIV&lt;br /&gt; 505 = DV&lt;br /&gt; 506 = DVI&lt;br /&gt; 507 = DVII&lt;br /&gt; 508 = DVIII&lt;br /&gt; 509 = DIX&lt;br /&gt; 510 = DX&lt;br /&gt; 511 = DXI&lt;br /&gt; 512 = DXII&lt;br /&gt; 513 = DXIII&lt;br /&gt; 514 = DXIV&lt;br /&gt; 515 = DXV&lt;br /&gt; 516 = DXVI&lt;br /&gt; 517 = DXVII&lt;br /&gt; 518 = DXVIII&lt;br /&gt; 519 = DXIX&lt;br /&gt; 520 = DXX&lt;br /&gt; 521 = DXXI&lt;br /&gt; 522 = DXXII&lt;br /&gt; 523 = DXXIII&lt;br /&gt; 524 = DXXIV&lt;br /&gt; 525 = DXXV&lt;br /&gt; 526 = DXXVI&lt;br /&gt; 527 = DXXVII&lt;br /&gt; 528 = DXXVIII&lt;br /&gt; 529 = DXXIX&lt;br /&gt; 530 = DXXX&lt;br /&gt; 531 = DXXXI&lt;br /&gt; 532 = DXXXII&lt;br /&gt; 533 = DXXXIII&lt;br /&gt; 534 = DXXXIV&lt;br /&gt; 535 = DXXXV&lt;br /&gt; 536 = DXXXVI&lt;br /&gt; 537 = DXXXVII&lt;br /&gt; 538 = DXXXVIII&lt;br /&gt; 539 = DXXXIX&lt;br /&gt; 540 = DXL&lt;br /&gt; 541 = DXLI&lt;br /&gt; 542 = DXLII&lt;br /&gt; 543 = DXLIII&lt;br /&gt; 544 = DXLIV&lt;br /&gt; 545 = DXLV&lt;br /&gt; 546 = DXLVI&lt;br /&gt; 547 = DXLVII&lt;br /&gt; 548 = DXLVIII&lt;br /&gt; 549 = DXLIX&lt;br /&gt; 550 = DL&lt;br /&gt; 551 = DLI&lt;br /&gt; 552 = DLII&lt;br /&gt; 553 = DLIII&lt;br /&gt; 554 = DLIV&lt;br /&gt; 555 = DLV&lt;br /&gt; 556 = DLVI&lt;br /&gt; 557 = DLVII&lt;br /&gt; 558 = DLVIII&lt;br /&gt; 559 = DLIX&lt;br /&gt; 560 = DLX&lt;br /&gt; 561 = DLXI&lt;br /&gt; 562 = DLXII&lt;br /&gt; 563 = DLXIII&lt;br /&gt; 564 = DLXIV&lt;br /&gt; 565 = DLXV&lt;br /&gt; 566 = DLXVI&lt;br /&gt; 567 = DLXVII&lt;br /&gt; 568 = DLXVIII&lt;br /&gt; 569 = DLXIX&lt;br /&gt; 570 = DLXX&lt;br /&gt; 571 = DLXXI&lt;br /&gt; 572 = DLXXII&lt;br /&gt; 573 = DLXXIII&lt;br /&gt; 574 = DLXXIV&lt;br /&gt; 575 = DLXXV&lt;br /&gt; 576 = DLXXVI&lt;br /&gt; 577 = DLXXVII&lt;br /&gt; 578 = DLXXVIII&lt;br /&gt; 579 = DLXXIX&lt;br /&gt; 580 = DLXXX&lt;br /&gt; 581 = DLXXXI&lt;br /&gt; 582 = DLXXXII&lt;br /&gt; 583 = DLXXXIII&lt;br /&gt; 584 = DLXXXIV&lt;br /&gt; 585 = DLXXXV&lt;br /&gt; 586 = DLXXXVI&lt;br /&gt; 587 = DLXXXVII&lt;br /&gt; 588 = DLXXXVIII&lt;br /&gt; 589 = DLXXXIX&lt;br /&gt; 590 = DXC&lt;br /&gt; 591 = DXCI&lt;br /&gt; 592 = DXCII&lt;br /&gt; 593 = DXCIII&lt;br /&gt; 594 = DXCIV&lt;br /&gt; 595 = DXCV&lt;br /&gt; 596 = DXCVI&lt;br /&gt; 597 = DXCVII&lt;br /&gt; 598 = DXCVIII&lt;br /&gt; 599 = DXCIX&lt;br /&gt; 600 = DC&lt;br /&gt; 601 = DCI&lt;br /&gt; 602 = DCII&lt;br /&gt; 603 = DCIII&lt;br /&gt; 604 = DCIV&lt;br /&gt; 605 = DCV&lt;br /&gt; 606 = DCVI&lt;br /&gt; 607 = DCVII&lt;br /&gt; 608 = DCVIII&lt;br /&gt; 609 = DCIX&lt;br /&gt; 610 = DCX&lt;br /&gt; 611 = DCXI&lt;br /&gt; 612 = DCXII&lt;br /&gt; 613 = DCXIII&lt;br /&gt; 614 = DCXIV&lt;br /&gt; 615 = DCXV&lt;br /&gt; 616 = DCXVI&lt;br /&gt; 617 = DCXVII&lt;br /&gt; 618 = DCXVIII&lt;br /&gt; 619 = DCXIX&lt;br /&gt; 620 = DCXX&lt;br /&gt; 621 = DCXXI&lt;br /&gt; 622 = DCXXII&lt;br /&gt; 623 = DCXXIII&lt;br /&gt; 624 = DCXXIV&lt;br /&gt; 625 = DCXXV&lt;br /&gt; 626 = DCXXVI&lt;br /&gt; 627 = DCXXVII&lt;br /&gt; 628 = DCXXVIII&lt;br /&gt; 629 = DCXXIX&lt;br /&gt; 630 = DCXXX&lt;br /&gt; 631 = DCXXXI&lt;br /&gt; 632 = DCXXXII&lt;br /&gt; 633 = DCXXXIII&lt;br /&gt; 634 = DCXXXIV&lt;br /&gt; 635 = DCXXXV&lt;br /&gt; 636 = DCXXXVI&lt;br /&gt; 637 = DCXXXVII&lt;br /&gt; 638 = DCXXXVIII&lt;br /&gt; 639 = DCXXXIX&lt;br /&gt; 640 = DCXL&lt;br /&gt; 641 = DCXLI&lt;br /&gt; 642 = DCXLII&lt;br /&gt; 643 = DCXLIII&lt;br /&gt; 644 = DCXLIV&lt;br /&gt; 645 = DCXLV&lt;br /&gt; 646 = DCXLVI&lt;br /&gt; 647 = DCXLVII&lt;br /&gt; 648 = DCXLVIII&lt;br /&gt; 649 = DCXLIX&lt;br /&gt; 650 = DCL&lt;br /&gt; 651 = DCLI&lt;br /&gt; 652 = DCLII&lt;br /&gt; 653 = DCLIII&lt;br /&gt; 654 = DCLIV&lt;br /&gt; 655 = DCLV&lt;br /&gt; 656 = DCLVI&lt;br /&gt; 657 = DCLVII&lt;br /&gt; 658 = DCLVIII&lt;br /&gt; 659 = DCLIX&lt;br /&gt; 660 = DCLX&lt;br /&gt; 661 = DCLXI&lt;br /&gt; 662 = DCLXII&lt;br /&gt; 663 = DCLXIII&lt;br /&gt; 664 = DCLXIV&lt;br /&gt; 665 = DCLXV&lt;br /&gt; 666 = DCLXVI&lt;br /&gt; 667 = DCLXVII&lt;br /&gt; 668 = DCLXVIII&lt;br /&gt; 669 = DCLXIX&lt;br /&gt; 670 = DCLXX&lt;br /&gt; 671 = DCLXXI&lt;br /&gt; 672 = DCLXXII&lt;br /&gt; 673 = DCLXXIII&lt;br /&gt; 674 = DCLXXIV&lt;br /&gt; 675 = DCLXXV&lt;br /&gt; 676 = DCLXXVI&lt;br /&gt; 677 = DCLXXVII&lt;br /&gt; 678 = DCLXXVIII&lt;br /&gt; 679 = DCLXXIX&lt;br /&gt; 680 = DCLXXX&lt;br /&gt; 681 = DCLXXXI&lt;br /&gt; 682 = DCLXXXII&lt;br /&gt; 683 = DCLXXXIII&lt;br /&gt; 684 = DCLXXXIV&lt;br /&gt; 685 = DCLXXXV&lt;br /&gt; 686 = DCLXXXVI&lt;br /&gt; 687 = DCLXXXVII&lt;br /&gt; 688 = DCLXXXVIII&lt;br /&gt; 689 = DCLXXXIX&lt;br /&gt; 690 = DCXC&lt;br /&gt; 691 = DCXCI&lt;br /&gt; 692 = DCXCII&lt;br /&gt; 693 = DCXCIII&lt;br /&gt; 694 = DCXCIV&lt;br /&gt; 695 = DCXCV&lt;br /&gt; 696 = DCXCVI&lt;br /&gt; 697 = DCXCVII&lt;br /&gt; 698 = DCXCVIII&lt;br /&gt; 699 = DCXCIX&lt;br /&gt; 700 = DCC&lt;br /&gt; 701 = DCCI&lt;br /&gt; 702 = DCCII&lt;br /&gt; 703 = DCCIII&lt;br /&gt; 704 = DCCIV&lt;br /&gt; 705 = DCCV&lt;br /&gt; 706 = DCCVI&lt;br /&gt; 707 = DCCVII&lt;br /&gt; 708 = DCCVIII&lt;br /&gt; 709 = DCCIX&lt;br /&gt; 710 = DCCX&lt;br /&gt; 711 = DCCXI&lt;br /&gt; 712 = DCCXII&lt;br /&gt; 713 = DCCXIII&lt;br /&gt; 714 = DCCXIV&lt;br /&gt; 715 = DCCXV&lt;br /&gt; 716 = DCCXVI&lt;br /&gt; 717 = DCCXVII&lt;br /&gt; 718 = DCCXVIII&lt;br /&gt; 719 = DCCXIX&lt;br /&gt; 720 = DCCXX&lt;br /&gt; 721 = DCCXXI&lt;br /&gt; 722 = DCCXXII&lt;br /&gt; 723 = DCCXXIII&lt;br /&gt; 724 = DCCXXIV&lt;br /&gt; 725 = DCCXXV&lt;br /&gt; 726 = DCCXXVI&lt;br /&gt; 727 = DCCXXVII&lt;br /&gt; 728 = DCCXXVIII&lt;br /&gt; 729 = DCCXXIX&lt;br /&gt; 730 = DCCXXX&lt;br /&gt; 731 = DCCXXXI&lt;br /&gt; 732 = DCCXXXII&lt;br /&gt; 733 = DCCXXXIII&lt;br /&gt; 734 = DCCXXXIV&lt;br /&gt; 735 = DCCXXXV&lt;br /&gt; 736 = DCCXXXVI&lt;br /&gt; 737 = DCCXXXVII&lt;br /&gt; 738 = DCCXXXVIII&lt;br /&gt; 739 = DCCXXXIX&lt;br /&gt; 740 = DCCXL&lt;br /&gt; 741 = DCCXLI&lt;br /&gt; 742 = DCCXLII&lt;br /&gt; 743 = DCCXLIII&lt;br /&gt; 744 = DCCXLIV&lt;br /&gt; 745 = DCCXLV&lt;br /&gt; 746 = DCCXLVI&lt;br /&gt; 747 = DCCXLVII&lt;br /&gt; 748 = DCCXLVIII&lt;br /&gt; 749 = DCCXLIX&lt;br /&gt; 750 = DCCL&lt;br /&gt; 751 = DCCLI&lt;br /&gt; 752 = DCCLII&lt;br /&gt; 753 = DCCLIII&lt;br /&gt; 754 = DCCLIV&lt;br /&gt; 755 = DCCLV&lt;br /&gt; 756 = DCCLVI&lt;br /&gt; 757 = DCCLVII&lt;br /&gt; 758 = DCCLVIII&lt;br /&gt; 759 = DCCLIX&lt;br /&gt; 760 = DCCLX&lt;br /&gt; 761 = DCCLXI&lt;br /&gt; 762 = DCCLXII&lt;br /&gt; 763 = DCCLXIII&lt;br /&gt; 764 = DCCLXIV&lt;br /&gt; 765 = DCCLXV&lt;br /&gt; 766 = DCCLXVI&lt;br /&gt; 767 = DCCLXVII&lt;br /&gt; 768 = DCCLXVIII&lt;br /&gt; 769 = DCCLXIX&lt;br /&gt; 770 = DCCLXX&lt;br /&gt; 771 = DCCLXXI&lt;br /&gt; 772 = DCCLXXII&lt;br /&gt; 773 = DCCLXXIII&lt;br /&gt; 774 = DCCLXXIV&lt;br /&gt; 775 = DCCLXXV&lt;br /&gt; 776 = DCCLXXVI&lt;br /&gt; 777 = DCCLXXVII&lt;br /&gt; 778 = DCCLXXVIII&lt;br /&gt; 779 = DCCLXXIX&lt;br /&gt; 780 = DCCLXXX&lt;br /&gt; 781 = DCCLXXXI&lt;br /&gt; 782 = DCCLXXXII&lt;br /&gt; 783 = DCCLXXXIII&lt;br /&gt; 784 = DCCLXXXIV&lt;br /&gt; 785 = DCCLXXXV&lt;br /&gt; 786 = DCCLXXXVI&lt;br /&gt; 787 = DCCLXXXVII&lt;br /&gt; 788 = DCCLXXXVIII&lt;br /&gt; 789 = DCCLXXXIX&lt;br /&gt; 790 = DCCXC&lt;br /&gt; 791 = DCCXCI&lt;br /&gt; 792 = DCCXCII&lt;br /&gt; 793 = DCCXCIII&lt;br /&gt; 794 = DCCXCIV&lt;br /&gt; 795 = DCCXCV&lt;br /&gt; 796 = DCCXCVI&lt;br /&gt; 797 = DCCXCVII&lt;br /&gt; 798 = DCCXCVIII&lt;br /&gt; 799 = DCCXCIX&lt;br /&gt; 800 = DCCC&lt;br /&gt; 801 = DCCCI&lt;br /&gt; 802 = DCCCII&lt;br /&gt; 803 = DCCCIII&lt;br /&gt; 804 = DCCCIV&lt;br /&gt; 805 = DCCCV&lt;br /&gt; 806 = DCCCVI&lt;br /&gt; 807 = DCCCVII&lt;br /&gt; 808 = DCCCVIII&lt;br /&gt; 809 = DCCCIX&lt;br /&gt; 810 = DCCCX&lt;br /&gt; 811 = DCCCXI&lt;br /&gt; 812 = DCCCXII&lt;br /&gt; 813 = DCCCXIII&lt;br /&gt; 814 = DCCCXIV&lt;br /&gt; 815 = DCCCXV&lt;br /&gt; 816 = DCCCXVI&lt;br /&gt; 817 = DCCCXVII&lt;br /&gt; 818 = DCCCXVIII&lt;br /&gt; 819 = DCCCXIX&lt;br /&gt; 820 = DCCCXX&lt;br /&gt; 821 = DCCCXXI&lt;br /&gt; 822 = DCCCXXII&lt;br /&gt; 823 = DCCCXXIII&lt;br /&gt; 824 = DCCCXXIV&lt;br /&gt; 825 = DCCCXXV&lt;br /&gt; 826 = DCCCXXVI&lt;br /&gt; 827 = DCCCXXVII&lt;br /&gt; 828 = DCCCXXVIII&lt;br /&gt; 829 = DCCCXXIX&lt;br /&gt; 830 = DCCCXXX&lt;br /&gt; 831 = DCCCXXXI&lt;br /&gt; 832 = DCCCXXXII&lt;br /&gt; 833 = DCCCXXXIII&lt;br /&gt; 834 = DCCCXXXIV&lt;br /&gt; 835 = DCCCXXXV&lt;br /&gt; 836 = DCCCXXXVI&lt;br /&gt; 837 = DCCCXXXVII&lt;br /&gt; 838 = DCCCXXXVIII&lt;br /&gt; 839 = DCCCXXXIX&lt;br /&gt; 840 = DCCCXL&lt;br /&gt; 841 = DCCCXLI&lt;br /&gt; 842 = DCCCXLII&lt;br /&gt; 843 = DCCCXLIII&lt;br /&gt; 844 = DCCCXLIV&lt;br /&gt; 845 = DCCCXLV&lt;br /&gt; 846 = DCCCXLVI&lt;br /&gt; 847 = DCCCXLVII&lt;br /&gt; 848 = DCCCXLVIII&lt;br /&gt; 849 = DCCCXLIX&lt;br /&gt; 850 = DCCCL&lt;br /&gt; 851 = DCCCLI&lt;br /&gt; 852 = DCCCLII&lt;br /&gt; 853 = DCCCLIII&lt;br /&gt; 854 = DCCCLIV&lt;br /&gt; 855 = DCCCLV&lt;br /&gt; 856 = DCCCLVI&lt;br /&gt; 857 = DCCCLVII&lt;br /&gt; 858 = DCCCLVIII&lt;br /&gt; 859 = DCCCLIX&lt;br /&gt; 860 = DCCCLX&lt;br /&gt; 861 = DCCCLXI&lt;br /&gt; 862 = DCCCLXII&lt;br /&gt; 863 = DCCCLXIII&lt;br /&gt; 864 = DCCCLXIV&lt;br /&gt; 865 = DCCCLXV&lt;br /&gt; 866 = DCCCLXVI&lt;br /&gt; 867 = DCCCLXVII&lt;br /&gt; 868 = DCCCLXVIII&lt;br /&gt; 869 = DCCCLXIX&lt;br /&gt; 870 = DCCCLXX&lt;br /&gt; 871 = DCCCLXXI&lt;br /&gt; 872 = DCCCLXXII&lt;br /&gt; 873 = DCCCLXXIII&lt;br /&gt; 874 = DCCCLXXIV&lt;br /&gt; 875 = DCCCLXXV&lt;br /&gt; 876 = DCCCLXXVI&lt;br /&gt; 877 = DCCCLXXVII&lt;br /&gt; 878 = DCCCLXXVIII&lt;br /&gt; 879 = DCCCLXXIX&lt;br /&gt; 880 = DCCCLXXX&lt;br /&gt; 881 = DCCCLXXXI&lt;br /&gt; 882 = DCCCLXXXII&lt;br /&gt; 883 = DCCCLXXXIII&lt;br /&gt; 884 = DCCCLXXXIV&lt;br /&gt; 885 = DCCCLXXXV&lt;br /&gt; 886 = DCCCLXXXVI&lt;br /&gt; 887 = DCCCLXXXVII&lt;br /&gt; 888 = DCCCLXXXVIII&lt;br /&gt; 889 = DCCCLXXXIX&lt;br /&gt; 890 = DCCCXC&lt;br /&gt; 891 = DCCCXCI&lt;br /&gt; 892 = DCCCXCII&lt;br /&gt; 893 = DCCCXCIII&lt;br /&gt; 894 = DCCCXCIV&lt;br /&gt; 895 = DCCCXCV&lt;br /&gt; 896 = DCCCXCVI&lt;br /&gt; 897 = DCCCXCVII&lt;br /&gt; 898 = DCCCXCVIII&lt;br /&gt; 899 = DCCCXCIX&lt;br /&gt; 900 = CM&lt;br /&gt; 901 = CMI&lt;br /&gt; 902 = CMII&lt;br /&gt; 903 = CMIII&lt;br /&gt; 904 = CMIV&lt;br /&gt; 905 = CMV&lt;br /&gt; 906 = CMVI&lt;br /&gt; 907 = CMVII&lt;br /&gt; 908 = CMVIII&lt;br /&gt; 909 = CMIX&lt;br /&gt; 910 = CMX&lt;br /&gt; 911 = CMXI&lt;br /&gt; 912 = CMXII&lt;br /&gt; 913 = CMXIII&lt;br /&gt; 914 = CMXIV&lt;br /&gt; 915 = CMXV&lt;br /&gt; 916 = CMXVI&lt;br /&gt; 917 = CMXVII&lt;br /&gt; 918 = CMXVIII&lt;br /&gt; 919 = CMXIX&lt;br /&gt; 920 = CMXX&lt;br /&gt; 921 = CMXXI&lt;br /&gt; 922 = CMXXII&lt;br /&gt; 923 = CMXXIII&lt;br /&gt; 924 = CMXXIV&lt;br /&gt; 925 = CMXXV&lt;br /&gt; 926 = CMXXVI&lt;br /&gt; 927 = CMXXVII&lt;br /&gt; 928 = CMXXVIII&lt;br /&gt; 929 = CMXXIX&lt;br /&gt; 930 = CMXXX&lt;br /&gt; 931 = CMXXXI&lt;br /&gt; 932 = CMXXXII&lt;br /&gt; 933 = CMXXXIII&lt;br /&gt; 934 = CMXXXIV&lt;br /&gt; 935 = CMXXXV&lt;br /&gt; 936 = CMXXXVI&lt;br /&gt; 937 = CMXXXVII&lt;br /&gt; 938 = CMXXXVIII&lt;br /&gt; 939 = CMXXXIX&lt;br /&gt; 940 = CMXL&lt;br /&gt; 941 = CMXLI&lt;br /&gt; 942 = CMXLII&lt;br /&gt; 943 = CMXLIII&lt;br /&gt; 944 = CMXLIV&lt;br /&gt; 945 = CMXLV&lt;br /&gt; 946 = CMXLVI&lt;br /&gt; 947 = CMXLVII&lt;br /&gt; 948 = CMXLVIII&lt;br /&gt; 949 = CMXLIX&lt;br /&gt; 950 = CML&lt;br /&gt; 951 = CMLI&lt;br /&gt; 952 = CMLII&lt;br /&gt; 953 = CMLIII&lt;br /&gt; 954 = CMLIV&lt;br /&gt; 955 = CMLV&lt;br /&gt; 956 = CMLVI&lt;br /&gt; 957 = CMLVII&lt;br /&gt; 958 = CMLVIII&lt;br /&gt; 959 = CMLIX&lt;br /&gt; 960 = CMLX&lt;br /&gt; 961 = CMLXI&lt;br /&gt; 962 = CMLXII&lt;br /&gt; 963 = CMLXIII&lt;br /&gt; 964 = CMLXIV&lt;br /&gt; 965 = CMLXV&lt;br /&gt; 966 = CMLXVI&lt;br /&gt; 967 = CMLXVII&lt;br /&gt; 968 = CMLXVIII&lt;br /&gt; 969 = CMLXIX&lt;br /&gt; 970 = CMLXX&lt;br /&gt; 971 = CMLXXI&lt;br /&gt; 972 = CMLXXII&lt;br /&gt; 973 = CMLXXIII&lt;br /&gt; 974 = CMLXXIV&lt;br /&gt; 975 = CMLXXV&lt;br /&gt; 976 = CMLXXVI&lt;br /&gt; 977 = CMLXXVII&lt;br /&gt; 978 = CMLXXVIII&lt;br /&gt; 979 = CMLXXIX&lt;br /&gt; 980 = CMLXXX&lt;br /&gt; 981 = CMLXXXI&lt;br /&gt; 982 = CMLXXXII&lt;br /&gt; 983 = CMLXXXIII&lt;br /&gt; 984 = CMLXXXIV&lt;br /&gt; 985 = CMLXXXV&lt;br /&gt; 986 = CMLXXXVI&lt;br /&gt; 987 = CMLXXXVII&lt;br /&gt; 988 = CMLXXXVIII&lt;br /&gt; 989 = CMLXXXIX&lt;br /&gt; 990 = CMXC&lt;br /&gt; 991 = CMXCI&lt;br /&gt; 992 = CMXCII&lt;br /&gt; 993 = CMXCIII&lt;br /&gt; 994 = CMXCIV&lt;br /&gt; 995 = CMXCV&lt;br /&gt; 996 = CMXCVI&lt;br /&gt; 997 = CMXCVII&lt;br /&gt; 998 = CMXCVIII&lt;br /&gt; 999 = CMXCIX&lt;br /&gt;1000 = M&lt;br /&gt;1001 = MI&lt;br /&gt;1002 = MII&lt;br /&gt;1003 = MIII&lt;br /&gt;1004 = MIV&lt;br /&gt;1005 = MV&lt;br /&gt;1006 = MVI&lt;br /&gt;1007 = MVII&lt;br /&gt;1008 = MVIII&lt;br /&gt;1009 = MIX&lt;br /&gt;1010 = MX&lt;br /&gt;1011 = MXI&lt;br /&gt;1012 = MXII&lt;br /&gt;1013 = MXIII&lt;br /&gt;1014 = MXIV&lt;br /&gt;1015 = MXV&lt;br /&gt;1016 = MXVI&lt;br /&gt;1017 = MXVII&lt;br /&gt;1018 = MXVIII&lt;br /&gt;1019 = MXIX&lt;br /&gt;1020 = MXX&lt;br /&gt;1021 = MXXI&lt;br /&gt;1022 = MXXII&lt;br /&gt;1023 = MXXIII&lt;br /&gt;1024 = MXXIV&lt;br /&gt;1025 = MXXV&lt;br /&gt;1026 = MXXVI&lt;br /&gt;1027 = MXXVII&lt;br /&gt;1028 = MXXVIII&lt;br /&gt;1029 = MXXIX&lt;br /&gt;1030 = MXXX&lt;br /&gt;1031 = MXXXI&lt;br /&gt;1032 = MXXXII&lt;br /&gt;1033 = MXXXIII&lt;br /&gt;1034 = MXXXIV&lt;br /&gt;1035 = MXXXV&lt;br /&gt;1036 = MXXXVI&lt;br /&gt;1037 = MXXXVII&lt;br /&gt;1038 = MXXXVIII&lt;br /&gt;1039 = MXXXIX&lt;br /&gt;1040 = MXL&lt;br /&gt;1041 = MXLI&lt;br /&gt;1042 = MXLII&lt;br /&gt;1043 = MXLIII&lt;br /&gt;1044 = MXLIV&lt;br /&gt;1045 = MXLV&lt;br /&gt;1046 = MXLVI&lt;br /&gt;1047 = MXLVII&lt;br /&gt;1048 = MXLVIII&lt;br /&gt;1049 = MXLIX&lt;br /&gt;1050 = ML&lt;br /&gt;1051 = MLI&lt;br /&gt;1052 = MLII&lt;br /&gt;1053 = MLIII&lt;br /&gt;1054 = MLIV&lt;br /&gt;1055 = MLV&lt;br /&gt;1056 = MLVI&lt;br /&gt;1057 = MLVII&lt;br /&gt;1058 = MLVIII&lt;br /&gt;1059 = MLIX&lt;br /&gt;1060 = MLX&lt;br /&gt;1061 = MLXI&lt;br /&gt;1062 = MLXII&lt;br /&gt;1063 = MLXIII&lt;br /&gt;1064 = MLXIV&lt;br /&gt;1065 = MLXV&lt;br /&gt;1066 = MLXVI&lt;br /&gt;1067 = MLXVII&lt;br /&gt;1068 = MLXVIII&lt;br /&gt;1069 = MLXIX&lt;br /&gt;1070 = MLXX&lt;br /&gt;1071 = MLXXI&lt;br /&gt;1072 = MLXXII&lt;br /&gt;1073 = MLXXIII&lt;br /&gt;1074 = MLXXIV&lt;br /&gt;1075 = MLXXV&lt;br /&gt;1076 = MLXXVI&lt;br /&gt;1077 = MLXXVII&lt;br /&gt;1078 = MLXXVIII&lt;br /&gt;1079 = MLXXIX&lt;br /&gt;1080 = MLXXX&lt;br /&gt;1081 = MLXXXI&lt;br /&gt;1082 = MLXXXII&lt;br /&gt;1083 = MLXXXIII&lt;br /&gt;1084 = MLXXXIV&lt;br /&gt;1085 = MLXXXV&lt;br /&gt;1086 = MLXXXVI&lt;br /&gt;1087 = MLXXXVII&lt;br /&gt;1088 = MLXXXVIII&lt;br /&gt;1089 = MLXXXIX&lt;br /&gt;1090 = MXC&lt;br /&gt;1091 = MXCI&lt;br /&gt;1092 = MXCII&lt;br /&gt;1093 = MXCIII&lt;br /&gt;1094 = MXCIV&lt;br /&gt;1095 = MXCV&lt;br /&gt;1096 = MXCVI&lt;br /&gt;1097 = MXCVII&lt;br /&gt;1098 = MXCVIII&lt;br /&gt;1099 = MXCIX&lt;br /&gt;1100 = MC&lt;br /&gt;1101 = MCI&lt;br /&gt;1102 = MCII&lt;br /&gt;1103 = MCIII&lt;br /&gt;1104 = MCIV&lt;br /&gt;1105 = MCV&lt;br /&gt;1106 = MCVI&lt;br /&gt;1107 = MCVII&lt;br /&gt;1108 = MCVIII&lt;br /&gt;1109 = MCIX&lt;br /&gt;1110 = MCX&lt;br /&gt;1111 = MCXI&lt;br /&gt;1112 = MCXII&lt;br /&gt;1113 = MCXIII&lt;br /&gt;1114 = MCXIV&lt;br /&gt;1115 = MCXV&lt;br /&gt;1116 = MCXVI&lt;br /&gt;1117 = MCXVII&lt;br /&gt;1118 = MCXVIII&lt;br /&gt;1119 = MCXIX&lt;br /&gt;1120 = MCXX&lt;br /&gt;1121 = MCXXI&lt;br /&gt;1122 = MCXXII&lt;br /&gt;1123 = MCXXIII&lt;br /&gt;1124 = MCXXIV&lt;br /&gt;1125 = MCXXV&lt;br /&gt;1126 = MCXXVI&lt;br /&gt;1127 = MCXXVII&lt;br /&gt;1128 = MCXXVIII&lt;br /&gt;1129 = MCXXIX&lt;br /&gt;1130 = MCXXX&lt;br /&gt;1131 = MCXXXI&lt;br /&gt;1132 = MCXXXII&lt;br /&gt;1133 = MCXXXIII&lt;br /&gt;1134 = MCXXXIV&lt;br /&gt;1135 = MCXXXV&lt;br /&gt;1136 = MCXXXVI&lt;br /&gt;1137 = MCXXXVII&lt;br /&gt;1138 = MCXXXVIII&lt;br /&gt;1139 = MCXXXIX&lt;br /&gt;1140 = MCXL&lt;br /&gt;1141 = MCXLI&lt;br /&gt;1142 = MCXLII&lt;br /&gt;1143 = MCXLIII&lt;br /&gt;1144 = MCXLIV&lt;br /&gt;1145 = MCXLV&lt;br /&gt;1146 = MCXLVI&lt;br /&gt;1147 = MCXLVII&lt;br /&gt;1148 = MCXLVIII&lt;br /&gt;1149 = MCXLIX&lt;br /&gt;1150 = MCL&lt;br /&gt;1151 = MCLI&lt;br /&gt;1152 = MCLII&lt;br /&gt;1153 = MCLIII&lt;br /&gt;1154 = MCLIV&lt;br /&gt;1155 = MCLV&lt;br /&gt;1156 = MCLVI&lt;br /&gt;1157 = MCLVII&lt;br /&gt;1158 = MCLVIII&lt;br /&gt;1159 = MCLIX&lt;br /&gt;1160 = MCLX&lt;br /&gt;1161 = MCLXI&lt;br /&gt;1162 = MCLXII&lt;br /&gt;1163 = MCLXIII&lt;br /&gt;1164 = MCLXIV&lt;br /&gt;1165 = MCLXV&lt;br /&gt;1166 = MCLXVI&lt;br /&gt;1167 = MCLXVII&lt;br /&gt;1168 = MCLXVIII&lt;br /&gt;1169 = MCLXIX&lt;br /&gt;1170 = MCLXX&lt;br /&gt;1171 = MCLXXI&lt;br /&gt;1172 = MCLXXII&lt;br /&gt;1173 = MCLXXIII&lt;br /&gt;1174 = MCLXXIV&lt;br /&gt;1175 = MCLXXV&lt;br /&gt;1176 = MCLXXVI&lt;br /&gt;1177 = MCLXXVII&lt;br /&gt;1178 = MCLXXVIII&lt;br /&gt;1179 = MCLXXIX&lt;br /&gt;1180 = MCLXXX&lt;br /&gt;1181 = MCLXXXI&lt;br /&gt;1182 = MCLXXXII&lt;br /&gt;1183 = MCLXXXIII&lt;br /&gt;1184 = MCLXXXIV&lt;br /&gt;1185 = MCLXXXV&lt;br /&gt;1186 = MCLXXXVI&lt;br /&gt;1187 = MCLXXXVII&lt;br /&gt;1188 = MCLXXXVIII&lt;br /&gt;1189 = MCLXXXIX&lt;br /&gt;1190 = MCXC&lt;br /&gt;1191 = MCXCI&lt;br /&gt;1192 = MCXCII&lt;br /&gt;1193 = MCXCIII&lt;br /&gt;1194 = MCXCIV&lt;br /&gt;1195 = MCXCV&lt;br /&gt;1196 = MCXCVI&lt;br /&gt;1197 = MCXCVII&lt;br /&gt;1198 = MCXCVIII&lt;br /&gt;1199 = MCXCIX&lt;br /&gt;1200 = MCC&lt;br /&gt;1201 = MCCI&lt;br /&gt;1202 = MCCII&lt;br /&gt;1203 = MCCIII&lt;br /&gt;1204 = MCCIV&lt;br /&gt;1205 = MCCV&lt;br /&gt;1206 = MCCVI&lt;br /&gt;1207 = MCCVII&lt;br /&gt;1208 = MCCVIII&lt;br /&gt;1209 = MCCIX&lt;br /&gt;1210 = MCCX&lt;br /&gt;1211 = MCCXI&lt;br /&gt;1212 = MCCXII&lt;br /&gt;1213 = MCCXIII&lt;br /&gt;1214 = MCCXIV&lt;br /&gt;1215 = MCCXV&lt;br /&gt;1216 = MCCXVI&lt;br /&gt;1217 = MCCXVII&lt;br /&gt;1218 = MCCXVIII&lt;br /&gt;1219 = MCCXIX&lt;br /&gt;1220 = MCCXX&lt;br /&gt;1221 = MCCXXI&lt;br /&gt;1222 = MCCXXII&lt;br /&gt;1223 = MCCXXIII&lt;br /&gt;1224 = MCCXXIV&lt;br /&gt;1225 = MCCXXV&lt;br /&gt;1226 = MCCXXVI&lt;br /&gt;1227 = MCCXXVII&lt;br /&gt;1228 = MCCXXVIII&lt;br /&gt;1229 = MCCXXIX&lt;br /&gt;1230 = MCCXXX&lt;br /&gt;1231 = MCCXXXI&lt;br /&gt;1232 = MCCXXXII&lt;br /&gt;1233 = MCCXXXIII&lt;br /&gt;1234 = MCCXXXIV&lt;br /&gt;1235 = MCCXXXV&lt;br /&gt;1236 = MCCXXXVI&lt;br /&gt;1237 = MCCXXXVII&lt;br /&gt;1238 = MCCXXXVIII&lt;br /&gt;1239 = MCCXXXIX&lt;br /&gt;1240 = MCCXL&lt;br /&gt;1241 = MCCXLI&lt;br /&gt;1242 = MCCXLII&lt;br /&gt;1243 = MCCXLIII&lt;br /&gt;1244 = MCCXLIV&lt;br /&gt;1245 = MCCXLV&lt;br /&gt;1246 = MCCXLVI&lt;br /&gt;1247 = MCCXLVII&lt;br /&gt;1248 = MCCXLVIII&lt;br /&gt;1249 = MCCXLIX&lt;br /&gt;1250 = MCCL&lt;br /&gt;1251 = MCCLI&lt;br /&gt;1252 = MCCLII&lt;br /&gt;1253 = MCCLIII&lt;br /&gt;1254 = MCCLIV&lt;br /&gt;1255 = MCCLV&lt;br /&gt;1256 = MCCLVI&lt;br /&gt;1257 = MCCLVII&lt;br /&gt;1258 = MCCLVIII&lt;br /&gt;1259 = MCCLIX&lt;br /&gt;1260 = MCCLX&lt;br /&gt;1261 = MCCLXI&lt;br /&gt;1262 = MCCLXII&lt;br /&gt;1263 = MCCLXIII&lt;br /&gt;1264 = MCCLXIV&lt;br /&gt;1265 = MCCLXV&lt;br /&gt;1266 = MCCLXVI&lt;br /&gt;1267 = MCCLXVII&lt;br /&gt;1268 = MCCLXVIII&lt;br /&gt;1269 = MCCLXIX&lt;br /&gt;1270 = MCCLXX&lt;br /&gt;1271 = MCCLXXI&lt;br /&gt;1272 = MCCLXXII&lt;br /&gt;1273 = MCCLXXIII&lt;br /&gt;1274 = MCCLXXIV&lt;br /&gt;1275 = MCCLXXV&lt;br /&gt;1276 = MCCLXXVI&lt;br /&gt;1277 = MCCLXXVII&lt;br /&gt;1278 = MCCLXXVIII&lt;br /&gt;1279 = MCCLXXIX&lt;br /&gt;1280 = MCCLXXX&lt;br /&gt;1281 = MCCLXXXI&lt;br /&gt;1282 = MCCLXXXII&lt;br /&gt;1283 = MCCLXXXIII&lt;br /&gt;1284 = MCCLXXXIV&lt;br /&gt;1285 = MCCLXXXV&lt;br /&gt;1286 = MCCLXXXVI&lt;br /&gt;1287 = MCCLXXXVII&lt;br /&gt;1288 = MCCLXXXVIII&lt;br /&gt;1289 = MCCLXXXIX&lt;br /&gt;1290 = MCCXC&lt;br /&gt;1291 = MCCXCI&lt;br /&gt;1292 = MCCXCII&lt;br /&gt;1293 = MCCXCIII&lt;br /&gt;1294 = MCCXCIV&lt;br /&gt;1295 = MCCXCV&lt;br /&gt;1296 = MCCXCVI&lt;br /&gt;1297 = MCCXCVII&lt;br /&gt;1298 = MCCXCVIII&lt;br /&gt;1299 = MCCXCIX&lt;br /&gt;1300 = MCCC&lt;br /&gt;1301 = MCCCI&lt;br /&gt;1302 = MCCCII&lt;br /&gt;1303 = MCCCIII&lt;br /&gt;1304 = MCCCIV&lt;br /&gt;1305 = MCCCV&lt;br /&gt;1306 = MCCCVI&lt;br /&gt;1307 = MCCCVII&lt;br /&gt;1308 = MCCCVIII&lt;br /&gt;1309 = MCCCIX&lt;br /&gt;1310 = MCCCX&lt;br /&gt;1311 = MCCCXI&lt;br /&gt;1312 = MCCCXII&lt;br /&gt;1313 = MCCCXIII&lt;br /&gt;1314 = MCCCXIV&lt;br /&gt;1315 = MCCCXV&lt;br /&gt;1316 = MCCCXVI&lt;br /&gt;1317 = MCCCXVII&lt;br /&gt;1318 = MCCCXVIII&lt;br /&gt;1319 = MCCCXIX&lt;br /&gt;1320 = MCCCXX&lt;br /&gt;1321 = MCCCXXI&lt;br /&gt;1322 = MCCCXXII&lt;br /&gt;1323 = MCCCXXIII&lt;br /&gt;1324 = MCCCXXIV&lt;br /&gt;1325 = MCCCXXV&lt;br /&gt;1326 = MCCCXXVI&lt;br /&gt;1327 = MCCCXXVII&lt;br /&gt;1328 = MCCCXXVIII&lt;br /&gt;1329 = MCCCXXIX&lt;br /&gt;1330 = MCCCXXX&lt;br /&gt;1331 = MCCCXXXI&lt;br /&gt;1332 = MCCCXXXII&lt;br /&gt;1333 = MCCCXXXIII&lt;br /&gt;1334 = MCCCXXXIV&lt;br /&gt;1335 = MCCCXXXV&lt;br /&gt;1336 = MCCCXXXVI&lt;br /&gt;1337 = MCCCXXXVII&lt;br /&gt;1338 = MCCCXXXVIII&lt;br /&gt;1339 = MCCCXXXIX&lt;br /&gt;1340 = MCCCXL&lt;br /&gt;1341 = MCCCXLI&lt;br /&gt;1342 = MCCCXLII&lt;br /&gt;1343 = MCCCXLIII&lt;br /&gt;1344 = MCCCXLIV&lt;br /&gt;1345 = MCCCXLV&lt;br /&gt;1346 = MCCCXLVI&lt;br /&gt;1347 = MCCCXLVII&lt;br /&gt;1348 = MCCCXLVIII&lt;br /&gt;1349 = MCCCXLIX&lt;br /&gt;1350 = MCCCL&lt;br /&gt;1351 = MCCCLI&lt;br /&gt;1352 = MCCCLII&lt;br /&gt;1353 = MCCCLIII&lt;br /&gt;1354 = MCCCLIV&lt;br /&gt;1355 = MCCCLV&lt;br /&gt;1356 = MCCCLVI&lt;br /&gt;1357 = MCCCLVII&lt;br /&gt;1358 = MCCCLVIII&lt;br /&gt;1359 = MCCCLIX&lt;br /&gt;1360 = MCCCLX&lt;br /&gt;1361 = MCCCLXI&lt;br /&gt;1362 = MCCCLXII&lt;br /&gt;1363 = MCCCLXIII&lt;br /&gt;1364 = MCCCLXIV&lt;br /&gt;1365 = MCCCLXV&lt;br /&gt;1366 = MCCCLXVI&lt;br /&gt;1367 = MCCCLXVII&lt;br /&gt;1368 = MCCCLXVIII&lt;br /&gt;1369 = MCCCLXIX&lt;br /&gt;1370 = MCCCLXX&lt;br /&gt;1371 = MCCCLXXI&lt;br /&gt;1372 = MCCCLXXII&lt;br /&gt;1373 = MCCCLXXIII&lt;br /&gt;1374 = MCCCLXXIV&lt;br /&gt;1375 = MCCCLXXV&lt;br /&gt;1376 = MCCCLXXVI&lt;br /&gt;1377 = MCCCLXXVII&lt;br /&gt;1378 = MCCCLXXVIII&lt;br /&gt;1379 = MCCCLXXIX&lt;br /&gt;1380 = MCCCLXXX&lt;br /&gt;1381 = MCCCLXXXI&lt;br /&gt;1382 = MCCCLXXXII&lt;br /&gt;1383 = MCCCLXXXIII&lt;br /&gt;1384 = MCCCLXXXIV&lt;br /&gt;1385 = MCCCLXXXV&lt;br /&gt;1386 = MCCCLXXXVI&lt;br /&gt;1387 = MCCCLXXXVII&lt;br /&gt;1388 = MCCCLXXXVIII&lt;br /&gt;1389 = MCCCLXXXIX&lt;br /&gt;1390 = MCCCXC&lt;br /&gt;1391 = MCCCXCI&lt;br /&gt;1392 = MCCCXCII&lt;br /&gt;1393 = MCCCXCIII&lt;br /&gt;1394 = MCCCXCIV&lt;br /&gt;1395 = MCCCXCV&lt;br /&gt;1396 = MCCCXCVI&lt;br /&gt;1397 = MCCCXCVII&lt;br /&gt;1398 = MCCCXCVIII&lt;br /&gt;1399 = MCCCXCIX&lt;br /&gt;1400 = MCD&lt;br /&gt;1401 = MCDI&lt;br /&gt;1402 = MCDII&lt;br /&gt;1403 = MCDIII&lt;br /&gt;1404 = MCDIV&lt;br /&gt;1405 = MCDV&lt;br /&gt;1406 = MCDVI&lt;br /&gt;1407 = MCDVII&lt;br /&gt;1408 = MCDVIII&lt;br /&gt;1409 = MCDIX&lt;br /&gt;1410 = MCDX&lt;br /&gt;1411 = MCDXI&lt;br /&gt;1412 = MCDXII&lt;br /&gt;1413 = MCDXIII&lt;br /&gt;1414 = MCDXIV&lt;br /&gt;1415 = MCDXV&lt;br /&gt;1416 = MCDXVI&lt;br /&gt;1417 = MCDXVII&lt;br /&gt;1418 = MCDXVIII&lt;br /&gt;1419 = MCDXIX&lt;br /&gt;1420 = MCDXX&lt;br /&gt;1421 = MCDXXI&lt;br /&gt;1422 = MCDXXII&lt;br /&gt;1423 = MCDXXIII&lt;br /&gt;1424 = MCDXXIV&lt;br /&gt;1425 = MCDXXV&lt;br /&gt;1426 = MCDXXVI&lt;br /&gt;1427 = MCDXXVII&lt;br /&gt;1428 = MCDXXVIII&lt;br /&gt;1429 = MCDXXIX&lt;br /&gt;1430 = MCDXXX&lt;br /&gt;1431 = MCDXXXI&lt;br /&gt;1432 = MCDXXXII&lt;br /&gt;1433 = MCDXXXIII&lt;br /&gt;1434 = MCDXXXIV&lt;br /&gt;1435 = MCDXXXV&lt;br /&gt;1436 = MCDXXXVI&lt;br /&gt;1437 = MCDXXXVII&lt;br /&gt;1438 = MCDXXXVIII&lt;br /&gt;1439 = MCDXXXIX&lt;br /&gt;1440 = MCDXL&lt;br /&gt;1441 = MCDXLI&lt;br /&gt;1442 = MCDXLII&lt;br /&gt;1443 = MCDXLIII&lt;br /&gt;1444 = MCDXLIV&lt;br /&gt;1445 = MCDXLV&lt;br /&gt;1446 = MCDXLVI&lt;br /&gt;1447 = MCDXLVII&lt;br /&gt;1448 = MCDXLVIII&lt;br /&gt;1449 = MCDXLIX&lt;br /&gt;1450 = MCDL&lt;br /&gt;1451 = MCDLI&lt;br /&gt;1452 = MCDLII&lt;br /&gt;1453 = MCDLIII&lt;br /&gt;1454 = MCDLIV&lt;br /&gt;1455 = MCDLV&lt;br /&gt;1456 = MCDLVI&lt;br /&gt;1457 = MCDLVII&lt;br /&gt;1458 = MCDLVIII&lt;br /&gt;1459 = MCDLIX&lt;br /&gt;1460 = MCDLX&lt;br /&gt;1461 = MCDLXI&lt;br /&gt;1462 = MCDLXII&lt;br /&gt;1463 = MCDLXIII&lt;br /&gt;1464 = MCDLXIV&lt;br /&gt;1465 = MCDLXV&lt;br /&gt;1466 = MCDLXVI&lt;br /&gt;1467 = MCDLXVII&lt;br /&gt;1468 = MCDLXVIII&lt;br /&gt;1469 = MCDLXIX&lt;br /&gt;1470 = MCDLXX&lt;br /&gt;1471 = MCDLXXI&lt;br /&gt;1472 = MCDLXXII&lt;br /&gt;1473 = MCDLXXIII&lt;br /&gt;1474 = MCDLXXIV&lt;br /&gt;1475 = MCDLXXV&lt;br /&gt;1476 = MCDLXXVI&lt;br /&gt;1477 = MCDLXXVII&lt;br /&gt;1478 = MCDLXXVIII&lt;br /&gt;1479 = MCDLXXIX&lt;br /&gt;1480 = MCDLXXX&lt;br /&gt;1481 = MCDLXXXI&lt;br /&gt;1482 = MCDLXXXII&lt;br /&gt;1483 = MCDLXXXIII&lt;br /&gt;1484 = MCDLXXXIV&lt;br /&gt;1485 = MCDLXXXV&lt;br /&gt;1486 = MCDLXXXVI&lt;br /&gt;1487 = MCDLXXXVII&lt;br /&gt;1488 = MCDLXXXVIII&lt;br /&gt;1489 = MCDLXXXIX&lt;br /&gt;1490 = MCDXC&lt;br /&gt;1491 = MCDXCI&lt;br /&gt;1492 = MCDXCII&lt;br /&gt;1493 = MCDXCIII&lt;br /&gt;1494 = MCDXCIV&lt;br /&gt;1495 = MCDXCV&lt;br /&gt;1496 = MCDXCVI&lt;br /&gt;1497 = MCDXCVII&lt;br /&gt;1498 = MCDXCVIII&lt;br /&gt;1499 = MCDXCIX&lt;br /&gt;1500 = MD&lt;br /&gt;1501 = MDI&lt;br /&gt;1502 = MDII&lt;br /&gt;1503 = MDIII&lt;br /&gt;1504 = MDIV&lt;br /&gt;1505 = MDV&lt;br /&gt;1506 = MDVI&lt;br /&gt;1507 = MDVII&lt;br /&gt;1508 = MDVIII&lt;br /&gt;1509 = MDIX&lt;br /&gt;1510 = MDX&lt;br /&gt;1511 = MDXI&lt;br /&gt;1512 = MDXII&lt;br /&gt;1513 = MDXIII&lt;br /&gt;1514 = MDXIV&lt;br /&gt;1515 = MDXV&lt;br /&gt;1516 = MDXVI&lt;br /&gt;1517 = MDXVII&lt;br /&gt;1518 = MDXVIII&lt;br /&gt;1519 = MDXIX&lt;br /&gt;1520 = MDXX&lt;br /&gt;1521 = MDXXI&lt;br /&gt;1522 = MDXXII&lt;br /&gt;1523 = MDXXIII&lt;br /&gt;1524 = MDXXIV&lt;br /&gt;1525 = MDXXV&lt;br /&gt;1526 = MDXXVI&lt;br /&gt;1527 = MDXXVII&lt;br /&gt;1528 = MDXXVIII&lt;br /&gt;1529 = MDXXIX&lt;br /&gt;1530 = MDXXX&lt;br /&gt;1531 = MDXXXI&lt;br /&gt;1532 = MDXXXII&lt;br /&gt;1533 = MDXXXIII&lt;br /&gt;1534 = MDXXXIV&lt;br /&gt;1535 = MDXXXV&lt;br /&gt;1536 = MDXXXVI&lt;br /&gt;1537 = MDXXXVII&lt;br /&gt;1538 = MDXXXVIII&lt;br /&gt;1539 = MDXXXIX&lt;br /&gt;1540 = MDXL&lt;br /&gt;1541 = MDXLI&lt;br /&gt;1542 = MDXLII&lt;br /&gt;1543 = MDXLIII&lt;br /&gt;1544 = MDXLIV&lt;br /&gt;1545 = MDXLV&lt;br /&gt;1546 = MDXLVI&lt;br /&gt;1547 = MDXLVII&lt;br /&gt;1548 = MDXLVIII&lt;br /&gt;1549 = MDXLIX&lt;br /&gt;1550 = MDL&lt;br /&gt;1551 = MDLI&lt;br /&gt;1552 = MDLII&lt;br /&gt;1553 = MDLIII&lt;br /&gt;1554 = MDLIV&lt;br /&gt;1555 = MDLV&lt;br /&gt;1556 = MDLVI&lt;br /&gt;1557 = MDLVII&lt;br /&gt;1558 = MDLVIII&lt;br /&gt;1559 = MDLIX&lt;br /&gt;1560 = MDLX&lt;br /&gt;1561 = MDLXI&lt;br /&gt;1562 = MDLXII&lt;br /&gt;1563 = MDLXIII&lt;br /&gt;1564 = MDLXIV&lt;br /&gt;1565 = MDLXV&lt;br /&gt;1566 = MDLXVI&lt;br /&gt;1567 = MDLXVII&lt;br /&gt;1568 = MDLXVIII&lt;br /&gt;1569 = MDLXIX&lt;br /&gt;1570 = MDLXX&lt;br /&gt;1571 = MDLXXI&lt;br /&gt;1572 = MDLXXII&lt;br /&gt;1573 = MDLXXIII&lt;br /&gt;1574 = MDLXXIV&lt;br /&gt;1575 = MDLXXV&lt;br /&gt;1576 = MDLXXVI&lt;br /&gt;1577 = MDLXXVII&lt;br /&gt;1578 = MDLXXVIII&lt;br /&gt;1579 = MDLXXIX&lt;br /&gt;1580 = MDLXXX&lt;br /&gt;1581 = MDLXXXI&lt;br /&gt;1582 = MDLXXXII&lt;br /&gt;1583 = MDLXXXIII&lt;br /&gt;1584 = MDLXXXIV&lt;br /&gt;1585 = MDLXXXV&lt;br /&gt;1586 = MDLXXXVI&lt;br /&gt;1587 = MDLXXXVII&lt;br /&gt;1588 = MDLXXXVIII&lt;br /&gt;1589 = MDLXXXIX&lt;br /&gt;1590 = MDXC&lt;br /&gt;1591 = MDXCI&lt;br /&gt;1592 = MDXCII&lt;br /&gt;1593 = MDXCIII&lt;br /&gt;1594 = MDXCIV&lt;br /&gt;1595 = MDXCV&lt;br /&gt;1596 = MDXCVI&lt;br /&gt;1597 = MDXCVII&lt;br /&gt;1598 = MDXCVIII&lt;br /&gt;1599 = MDXCIX&lt;br /&gt;1600 = MDC&lt;br /&gt;1601 = MDCI&lt;br /&gt;1602 = MDCII&lt;br /&gt;1603 = MDCIII&lt;br /&gt;1604 = MDCIV&lt;br /&gt;1605 = MDCV&lt;br /&gt;1606 = MDCVI&lt;br /&gt;1607 = MDCVII&lt;br /&gt;1608 = MDCVIII&lt;br /&gt;1609 = MDCIX&lt;br /&gt;1610 = MDCX&lt;br /&gt;1611 = MDCXI&lt;br /&gt;1612 = MDCXII&lt;br /&gt;1613 = MDCXIII&lt;br /&gt;1614 = MDCXIV&lt;br /&gt;1615 = MDCXV&lt;br /&gt;1616 = MDCXVI&lt;br /&gt;1617 = MDCXVII&lt;br /&gt;1618 = MDCXVIII&lt;br /&gt;1619 = MDCXIX&lt;br /&gt;1620 = MDCXX&lt;br /&gt;1621 = MDCXXI&lt;br /&gt;1622 = MDCXXII&lt;br /&gt;1623 = MDCXXIII&lt;br /&gt;1624 = MDCXXIV&lt;br /&gt;1625 = MDCXXV&lt;br /&gt;1626 = MDCXXVI&lt;br /&gt;1627 = MDCXXVII&lt;br /&gt;1628 = MDCXXVIII&lt;br /&gt;1629 = MDCXXIX&lt;br /&gt;1630 = MDCXXX&lt;br /&gt;1631 = MDCXXXI&lt;br /&gt;1632 = MDCXXXII&lt;br /&gt;1633 = MDCXXXIII&lt;br /&gt;1634 = MDCXXXIV&lt;br /&gt;1635 = MDCXXXV&lt;br /&gt;1636 = MDCXXXVI&lt;br /&gt;1637 = MDCXXXVII&lt;br /&gt;1638 = MDCXXXVIII&lt;br /&gt;1639 = MDCXXXIX&lt;br /&gt;1640 = MDCXL&lt;br /&gt;1641 = MDCXLI&lt;br /&gt;1642 = MDCXLII&lt;br /&gt;1643 = MDCXLIII&lt;br /&gt;1644 = MDCXLIV&lt;br /&gt;1645 = MDCXLV&lt;br /&gt;1646 = MDCXLVI&lt;br /&gt;1647 = MDCXLVII&lt;br /&gt;1648 = MDCXLVIII&lt;br /&gt;1649 = MDCXLIX&lt;br /&gt;1650 = MDCL&lt;br /&gt;1651 = MDCLI&lt;br /&gt;1652 = MDCLII&lt;br /&gt;1653 = MDCLIII&lt;br /&gt;1654 = MDCLIV&lt;br /&gt;1655 = MDCLV&lt;br /&gt;1656 = MDCLVI&lt;br /&gt;1657 = MDCLVII&lt;br /&gt;1658 = MDCLVIII&lt;br /&gt;1659 = MDCLIX&lt;br /&gt;1660 = MDCLX&lt;br /&gt;1661 = MDCLXI&lt;br /&gt;1662 = MDCLXII&lt;br /&gt;1663 = MDCLXIII&lt;br /&gt;1664 = MDCLXIV&lt;br /&gt;1665 = MDCLXV&lt;br /&gt;1666 = MDCLXVI&lt;br /&gt;1667 = MDCLXVII&lt;br /&gt;1668 = MDCLXVIII&lt;br /&gt;1669 = MDCLXIX&lt;br /&gt;1670 = MDCLXX&lt;br /&gt;1671 = MDCLXXI&lt;br /&gt;1672 = MDCLXXII&lt;br /&gt;1673 = MDCLXXIII&lt;br /&gt;1674 = MDCLXXIV&lt;br /&gt;1675 = MDCLXXV&lt;br /&gt;1676 = MDCLXXVI&lt;br /&gt;1677 = MDCLXXVII&lt;br /&gt;1678 = MDCLXXVIII&lt;br /&gt;1679 = MDCLXXIX&lt;br /&gt;1680 = MDCLXXX&lt;br /&gt;1681 = MDCLXXXI&lt;br /&gt;1682 = MDCLXXXII&lt;br /&gt;1683 = MDCLXXXIII&lt;br /&gt;1684 = MDCLXXXIV&lt;br /&gt;1685 = MDCLXXXV&lt;br /&gt;1686 = MDCLXXXVI&lt;br /&gt;1687 = MDCLXXXVII&lt;br /&gt;1688 = MDCLXXXVIII&lt;br /&gt;1689 = MDCLXXXIX&lt;br /&gt;1690 = MDCXC&lt;br /&gt;1691 = MDCXCI&lt;br /&gt;1692 = MDCXCII&lt;br /&gt;1693 = MDCXCIII&lt;br /&gt;1694 = MDCXCIV&lt;br /&gt;1695 = MDCXCV&lt;br /&gt;1696 = MDCXCVI&lt;br /&gt;1697 = MDCXCVII&lt;br /&gt;1698 = MDCXCVIII&lt;br /&gt;1699 = MDCXCIX&lt;br /&gt;1700 = MDCC&lt;br /&gt;1701 = MDCCI&lt;br /&gt;1702 = MDCCII&lt;br /&gt;1703 = MDCCIII&lt;br /&gt;1704 = MDCCIV&lt;br /&gt;1705 = MDCCV&lt;br /&gt;1706 = MDCCVI&lt;br /&gt;1707 = MDCCVII&lt;br /&gt;1708 = MDCCVIII&lt;br /&gt;1709 = MDCCIX&lt;br /&gt;1710 = MDCCX&lt;br /&gt;1711 = MDCCXI&lt;br /&gt;1712 = MDCCXII&lt;br /&gt;1713 = MDCCXIII&lt;br /&gt;1714 = MDCCXIV&lt;br /&gt;1715 = MDCCXV&lt;br /&gt;1716 = MDCCXVI&lt;br /&gt;1717 = MDCCXVII&lt;br /&gt;1718 = MDCCXVIII&lt;br /&gt;1719 = MDCCXIX&lt;br /&gt;1720 = MDCCXX&lt;br /&gt;1721 = MDCCXXI&lt;br /&gt;1722 = MDCCXXII&lt;br /&gt;1723 = MDCCXXIII&lt;br /&gt;1724 = MDCCXXIV&lt;br /&gt;1725 = MDCCXXV&lt;br /&gt;1726 = MDCCXXVI&lt;br /&gt;1727 = MDCCXXVII&lt;br /&gt;1728 = MDCCXXVIII&lt;br /&gt;1729 = MDCCXXIX&lt;br /&gt;1730 = MDCCXXX&lt;br /&gt;1731 = MDCCXXXI&lt;br /&gt;1732 = MDCCXXXII&lt;br /&gt;1733 = MDCCXXXIII&lt;br /&gt;1734 = MDCCXXXIV&lt;br /&gt;1735 = MDCCXXXV&lt;br /&gt;1736 = MDCCXXXVI&lt;br /&gt;1737 = MDCCXXXVII&lt;br /&gt;1738 = MDCCXXXVIII&lt;br /&gt;1739 = MDCCXXXIX&lt;br /&gt;1740 = MDCCXL&lt;br /&gt;1741 = MDCCXLI&lt;br /&gt;1742 = MDCCXLII&lt;br /&gt;1743 = MDCCXLIII&lt;br /&gt;1744 = MDCCXLIV&lt;br /&gt;1745 = MDCCXLV&lt;br /&gt;1746 = MDCCXLVI&lt;br /&gt;1747 = MDCCXLVII&lt;br /&gt;1748 = MDCCXLVIII&lt;br /&gt;1749 = MDCCXLIX&lt;br /&gt;1750 = MDCCL&lt;br /&gt;1751 = MDCCLI&lt;br /&gt;1752 = MDCCLII&lt;br /&gt;1753 = MDCCLIII&lt;br /&gt;1754 = MDCCLIV&lt;br /&gt;1755 = MDCCLV&lt;br /&gt;1756 = MDCCLVI&lt;br /&gt;1757 = MDCCLVII&lt;br /&gt;1758 = MDCCLVIII&lt;br /&gt;1759 = MDCCLIX&lt;br /&gt;1760 = MDCCLX&lt;br /&gt;1761 = MDCCLXI&lt;br /&gt;1762 = MDCCLXII&lt;br /&gt;1763 = MDCCLXIII&lt;br /&gt;1764 = MDCCLXIV&lt;br /&gt;1765 = MDCCLXV&lt;br /&gt;1766 = MDCCLXVI&lt;br /&gt;1767 = MDCCLXVII&lt;br /&gt;1768 = MDCCLXVIII&lt;br /&gt;1769 = MDCCLXIX&lt;br /&gt;1770 = MDCCLXX&lt;br /&gt;1771 = MDCCLXXI&lt;br /&gt;1772 = MDCCLXXII&lt;br /&gt;1773 = MDCCLXXIII&lt;br /&gt;1774 = MDCCLXXIV&lt;br /&gt;1775 = MDCCLXXV&lt;br /&gt;1776 = MDCCLXXVI&lt;br /&gt;1777 = MDCCLXXVII&lt;br /&gt;1778 = MDCCLXXVIII&lt;br /&gt;1779 = MDCCLXXIX&lt;br /&gt;1780 = MDCCLXXX&lt;br /&gt;1781 = MDCCLXXXI&lt;br /&gt;1782 = MDCCLXXXII&lt;br /&gt;1783 = MDCCLXXXIII&lt;br /&gt;1784 = MDCCLXXXIV&lt;br /&gt;1785 = MDCCLXXXV&lt;br /&gt;1786 = MDCCLXXXVI&lt;br /&gt;1787 = MDCCLXXXVII&lt;br /&gt;1788 = MDCCLXXXVIII&lt;br /&gt;1789 = MDCCLXXXIX&lt;br /&gt;1790 = MDCCXC&lt;br /&gt;1791 = MDCCXCI&lt;br /&gt;1792 = MDCCXCII&lt;br /&gt;1793 = MDCCXCIII&lt;br /&gt;1794 = MDCCXCIV&lt;br /&gt;1795 = MDCCXCV&lt;br /&gt;1796 = MDCCXCVI&lt;br /&gt;1797 = MDCCXCVII&lt;br /&gt;1798 = MDCCXCVIII&lt;br /&gt;1799 = MDCCXCIX&lt;br /&gt;1800 = MDCCC&lt;br /&gt;1801 = MDCCCI&lt;br /&gt;1802 = MDCCCII&lt;br /&gt;1803 = MDCCCIII&lt;br /&gt;1804 = MDCCCIV&lt;br /&gt;1805 = MDCCCV&lt;br /&gt;1806 = MDCCCVI&lt;br /&gt;1807 = MDCCCVII&lt;br /&gt;1808 = MDCCCVIII&lt;br /&gt;1809 = MDCCCIX&lt;br /&gt;1810 = MDCCCX&lt;br /&gt;1811 = MDCCCXI&lt;br /&gt;1812 = MDCCCXII&lt;br /&gt;1813 = MDCCCXIII&lt;br /&gt;1814 = MDCCCXIV&lt;br /&gt;1815 = MDCCCXV&lt;br /&gt;1816 = MDCCCXVI&lt;br /&gt;1817 = MDCCCXVII&lt;br /&gt;1818 = MDCCCXVIII&lt;br /&gt;1819 = MDCCCXIX&lt;br /&gt;1820 = MDCCCXX&lt;br /&gt;1821 = MDCCCXXI&lt;br /&gt;1822 = MDCCCXXII&lt;br /&gt;1823 = MDCCCXXIII&lt;br /&gt;1824 = MDCCCXXIV&lt;br /&gt;1825 = MDCCCXXV&lt;br /&gt;1826 = MDCCCXXVI&lt;br /&gt;1827 = MDCCCXXVII&lt;br /&gt;1828 = MDCCCXXVIII&lt;br /&gt;1829 = MDCCCXXIX&lt;br /&gt;1830 = MDCCCXXX&lt;br /&gt;1831 = MDCCCXXXI&lt;br /&gt;1832 = MDCCCXXXII&lt;br /&gt;1833 = MDCCCXXXIII&lt;br /&gt;1834 = MDCCCXXXIV&lt;br /&gt;1835 = MDCCCXXXV&lt;br /&gt;1836 = MDCCCXXXVI&lt;br /&gt;1837 = MDCCCXXXVII&lt;br /&gt;1838 = MDCCCXXXVIII&lt;br /&gt;1839 = MDCCCXXXIX&lt;br /&gt;1840 = MDCCCXL&lt;br /&gt;1841 = MDCCCXLI&lt;br /&gt;1842 = MDCCCXLII&lt;br /&gt;1843 = MDCCCXLIII&lt;br /&gt;1844 = MDCCCXLIV&lt;br /&gt;1845 = MDCCCXLV&lt;br /&gt;1846 = MDCCCXLVI&lt;br /&gt;1847 = MDCCCXLVII&lt;br /&gt;1848 = MDCCCXLVIII&lt;br /&gt;1849 = MDCCCXLIX&lt;br /&gt;1850 = MDCCCL&lt;br /&gt;1851 = MDCCCLI&lt;br /&gt;1852 = MDCCCLII&lt;br /&gt;1853 = MDCCCLIII&lt;br /&gt;1854 = MDCCCLIV&lt;br /&gt;1855 = MDCCCLV&lt;br /&gt;1856 = MDCCCLVI&lt;br /&gt;1857 = MDCCCLVII&lt;br /&gt;1858 = MDCCCLVIII&lt;br /&gt;1859 = MDCCCLIX&lt;br /&gt;1860 = MDCCCLX&lt;br /&gt;1861 = MDCCCLXI&lt;br /&gt;1862 = MDCCCLXII&lt;br /&gt;1863 = MDCCCLXIII&lt;br /&gt;1864 = MDCCCLXIV&lt;br /&gt;1865 = MDCCCLXV&lt;br /&gt;1866 = MDCCCLXVI&lt;br /&gt;1867 = MDCCCLXVII&lt;br /&gt;1868 = MDCCCLXVIII&lt;br /&gt;1869 = MDCCCLXIX&lt;br /&gt;1870 = MDCCCLXX&lt;br /&gt;1871 = MDCCCLXXI&lt;br /&gt;1872 = MDCCCLXXII&lt;br /&gt;1873 = MDCCCLXXIII&lt;br /&gt;1874 = MDCCCLXXIV&lt;br /&gt;1875 = MDCCCLXXV&lt;br /&gt;1876 = MDCCCLXXVI&lt;br /&gt;1877 = MDCCCLXXVII&lt;br /&gt;1878 = MDCCCLXXVIII&lt;br /&gt;1879 = MDCCCLXXIX&lt;br /&gt;1880 = MDCCCLXXX&lt;br /&gt;1881 = MDCCCLXXXI&lt;br /&gt;1882 = MDCCCLXXXII&lt;br /&gt;1883 = MDCCCLXXXIII&lt;br /&gt;1884 = MDCCCLXXXIV&lt;br /&gt;1885 = MDCCCLXXXV&lt;br /&gt;1886 = MDCCCLXXXVI&lt;br /&gt;1887 = MDCCCLXXXVII&lt;br /&gt;1888 = MDCCCLXXXVIII&lt;br /&gt;1889 = MDCCCLXXXIX&lt;br /&gt;1890 = MDCCCXC&lt;br /&gt;1891 = MDCCCXCI&lt;br /&gt;1892 = MDCCCXCII&lt;br /&gt;1893 = MDCCCXCIII&lt;br /&gt;1894 = MDCCCXCIV&lt;br /&gt;1895 = MDCCCXCV&lt;br /&gt;1896 = MDCCCXCVI&lt;br /&gt;1897 = MDCCCXCVII&lt;br /&gt;1898 = MDCCCXCVIII&lt;br /&gt;1899 = MDCCCXCIX&lt;br /&gt;1900 = MCM&lt;br /&gt;1901 = MCMI&lt;br /&gt;1902 = MCMII&lt;br /&gt;1903 = MCMIII&lt;br /&gt;1904 = MCMIV&lt;br /&gt;1905 = MCMV&lt;br /&gt;1906 = MCMVI&lt;br /&gt;1907 = MCMVII&lt;br /&gt;1908 = MCMVIII&lt;br /&gt;1909 = MCMIX&lt;br /&gt;1910 = MCMX&lt;br /&gt;1911 = MCMXI&lt;br /&gt;1912 = MCMXII&lt;br /&gt;1913 = MCMXIII&lt;br /&gt;1914 = MCMXIV&lt;br /&gt;1915 = MCMXV&lt;br /&gt;1916 = MCMXVI&lt;br /&gt;1917 = MCMXVII&lt;br /&gt;1918 = MCMXVIII&lt;br /&gt;1919 = MCMXIX&lt;br /&gt;1920 = MCMXX&lt;br /&gt;1921 = MCMXXI&lt;br /&gt;1922 = MCMXXII&lt;br /&gt;1923 = MCMXXIII&lt;br /&gt;1924 = MCMXXIV&lt;br /&gt;1925 = MCMXXV&lt;br /&gt;1926 = MCMXXVI&lt;br /&gt;1927 = MCMXXVII&lt;br /&gt;1928 = MCMXXVIII&lt;br /&gt;1929 = MCMXXIX&lt;br /&gt;1930 = MCMXXX&lt;br /&gt;1931 = MCMXXXI&lt;br /&gt;1932 = MCMXXXII&lt;br /&gt;1933 = MCMXXXIII&lt;br /&gt;1934 = MCMXXXIV&lt;br /&gt;1935 = MCMXXXV&lt;br /&gt;1936 = MCMXXXVI&lt;br /&gt;1937 = MCMXXXVII&lt;br /&gt;1938 = MCMXXXVIII&lt;br /&gt;1939 = MCMXXXIX&lt;br /&gt;1940 = MCMXL&lt;br /&gt;1941 = MCMXLI&lt;br /&gt;1942 = MCMXLII&lt;br /&gt;1943 = MCMXLIII&lt;br /&gt;1944 = MCMXLIV&lt;br /&gt;1945 = MCMXLV&lt;br /&gt;1946 = MCMXLVI&lt;br /&gt;1947 = MCMXLVII&lt;br /&gt;1948 = MCMXLVIII&lt;br /&gt;1949 = MCMXLIX&lt;br /&gt;1950 = MCML&lt;br /&gt;1951 = MCMLI&lt;br /&gt;1952 = MCMLII&lt;br /&gt;1953 = MCMLIII&lt;br /&gt;1954 = MCMLIV&lt;br /&gt;1955 = MCMLV&lt;br /&gt;1956 = MCMLVI&lt;br /&gt;1957 = MCMLVII&lt;br /&gt;1958 = MCMLVIII&lt;br /&gt;1959 = MCMLIX&lt;br /&gt;1960 = MCMLX&lt;br /&gt;1961 = MCMLXI&lt;br /&gt;1962 = MCMLXII&lt;br /&gt;1963 = MCMLXIII&lt;br /&gt;1964 = MCMLXIV&lt;br /&gt;1965 = MCMLXV&lt;br /&gt;1966 = MCMLXVI&lt;br /&gt;1967 = MCMLXVII&lt;br /&gt;1968 = MCMLXVIII&lt;br /&gt;1969 = MCMLXIX&lt;br /&gt;1970 = MCMLXX&lt;br /&gt;1971 = MCMLXXI&lt;br /&gt;1972 = MCMLXXII&lt;br /&gt;1973 = MCMLXXIII&lt;br /&gt;1974 = MCMLXXIV&lt;br /&gt;1975 = MCMLXXV&lt;br /&gt;1976 = MCMLXXVI&lt;br /&gt;1977 = MCMLXXVII&lt;br /&gt;1978 = MCMLXXVIII&lt;br /&gt;1979 = MCMLXXIX&lt;br /&gt;1980 = MCMLXXX&lt;br /&gt;1981 = MCMLXXXI&lt;br /&gt;1982 = MCMLXXXII&lt;br /&gt;1983 = MCMLXXXIII&lt;br /&gt;1984 = MCMLXXXIV&lt;br /&gt;1985 = MCMLXXXV&lt;br /&gt;1986 = MCMLXXXVI&lt;br /&gt;1987 = MCMLXXXVII&lt;br /&gt;1988 = MCMLXXXVIII&lt;br /&gt;1989 = MCMLXXXIX&lt;br /&gt;1990 = MCMXC&lt;br /&gt;1991 = MCMXCI&lt;br /&gt;1992 = MCMXCII&lt;br /&gt;1993 = MCMXCIII&lt;br /&gt;1994 = MCMXCIV&lt;br /&gt;1995 = MCMXCV&lt;br /&gt;1996 = MCMXCVI&lt;br /&gt;1997 = MCMXCVII&lt;br /&gt;1998 = MCMXCVIII&lt;br /&gt;1999 = MCMXCIX&lt;br /&gt;2000 = MM&lt;br /&gt;2001 = MMI&lt;br /&gt;2002 = MMII&lt;br /&gt;2003 = MMIII&lt;br /&gt;2004 = MMIV&lt;br /&gt;2005 = MMV&lt;br /&gt;2006 = MMVI&lt;br /&gt;2007 = MMVII&lt;br /&gt;2008 = MMVIII&lt;br /&gt;2009 = MMIX&lt;br /&gt;2010 = MMX&lt;br /&gt;2011 = MMXI&lt;br /&gt;2012 = MMXII&lt;br /&gt;2013 = MMXIII&lt;br /&gt;2014 = MMXIV&lt;br /&gt;2015 = MMXV&lt;br /&gt;2016 = MMXVI&lt;br /&gt;2017 = MMXVII&lt;br /&gt;2018 = MMXVIII&lt;br /&gt;2019 = MMXIX&lt;br /&gt;2020 = MMXX&lt;br /&gt;2021 = MMXXI&lt;br /&gt;2022 = MMXXII&lt;br /&gt;2023 = MMXXIII&lt;br /&gt;2024 = MMXXIV&lt;br /&gt;2025 = MMXXV&lt;br /&gt;2026 = MMXXVI&lt;br /&gt;2027 = MMXXVII&lt;br /&gt;2028 = MMXXVIII&lt;br /&gt;2029 = MMXXIX&lt;br /&gt;2030 = MMXXX&lt;br /&gt;2031 = MMXXXI&lt;br /&gt;2032 = MMXXXII&lt;br /&gt;2033 = MMXXXIII&lt;br /&gt;2034 = MMXXXIV&lt;br /&gt;2035 = MMXXXV&lt;br /&gt;2036 = MMXXXVI&lt;br /&gt;2037 = MMXXXVII&lt;br /&gt;2038 = MMXXXVIII&lt;br /&gt;2039 = MMXXXIX&lt;br /&gt;2040 = MMXL&lt;br /&gt;2041 = MMXLI&lt;br /&gt;2042 = MMXLII&lt;br /&gt;2043 = MMXLIII&lt;br /&gt;2044 = MMXLIV&lt;br /&gt;2045 = MMXLV&lt;br /&gt;2046 = MMXLVI&lt;br /&gt;2047 = MMXLVII&lt;br /&gt;2048 = MMXLVIII&lt;br /&gt;2049 = MMXLIX&lt;br /&gt;2050 = MML&lt;br /&gt;2051 = MMLI&lt;br /&gt;2052 = MMLII&lt;br /&gt;2053 = MMLIII&lt;br /&gt;2054 = MMLIV&lt;br /&gt;2055 = MMLV&lt;br /&gt;2056 = MMLVI&lt;br /&gt;2057 = MMLVII&lt;br /&gt;2058 = MMLVIII&lt;br /&gt;2059 = MMLIX&lt;br /&gt;2060 = MMLX&lt;br /&gt;2061 = MMLXI&lt;br /&gt;2062 = MMLXII&lt;br /&gt;2063 = MMLXIII&lt;br /&gt;2064 = MMLXIV&lt;br /&gt;2065 = MMLXV&lt;br /&gt;2066 = MMLXVI&lt;br /&gt;2067 = MMLXVII&lt;br /&gt;2068 = MMLXVIII&lt;br /&gt;2069 = MMLXIX&lt;br /&gt;2070 = MMLXX&lt;br /&gt;2071 = MMLXXI&lt;br /&gt;2072 = MMLXXII&lt;br /&gt;2073 = MMLXXIII&lt;br /&gt;2074 = MMLXXIV&lt;br /&gt;2075 = MMLXXV&lt;br /&gt;2076 = MMLXXVI&lt;br /&gt;2077 = MMLXXVII&lt;br /&gt;2078 = MMLXXVIII&lt;br /&gt;2079 = MMLXXIX&lt;br /&gt;2080 = MMLXXX&lt;br /&gt;2081 = MMLXXXI&lt;br /&gt;2082 = MMLXXXII&lt;br /&gt;2083 = MMLXXXIII&lt;br /&gt;2084 = MMLXXXIV&lt;br /&gt;2085 = MMLXXXV&lt;br /&gt;2086 = MMLXXXVI&lt;br /&gt;2087 = MMLXXXVII&lt;br /&gt;2088 = MMLXXXVIII&lt;br /&gt;2089 = MMLXXXIX&lt;br /&gt;2090 = MMXC&lt;br /&gt;2091 = MMXCI&lt;br /&gt;2092 = MMXCII&lt;br /&gt;2093 = MMXCIII&lt;br /&gt;2094 = MMXCIV&lt;br /&gt;2095 = MMXCV&lt;br /&gt;2096 = MMXCVI&lt;br /&gt;2097 = MMXCVII&lt;br /&gt;2098 = MMXCVIII&lt;br /&gt;2099 = MMXCIX&lt;br /&gt;2100 = MMC&lt;br /&gt;2101 = MMCI&lt;br /&gt;2102 = MMCII&lt;br /&gt;2103 = MMCIII&lt;br /&gt;2104 = MMCIV&lt;br /&gt;2105 = MMCV&lt;br /&gt;2106 = MMCVI&lt;br /&gt;2107 = MMCVII&lt;br /&gt;2108 = MMCVIII&lt;br /&gt;2109 = MMCIX&lt;br /&gt;2110 = MMCX&lt;br /&gt;2111 = MMCXI&lt;br /&gt;2112 = MMCXII&lt;br /&gt;2113 = MMCXIII&lt;br /&gt;2114 = MMCXIV&lt;br /&gt;2115 = MMCXV&lt;br /&gt;2116 = MMCXVI&lt;br /&gt;2117 = MMCXVII&lt;br /&gt;2118 = MMCXVIII&lt;br /&gt;2119 = MMCXIX&lt;br /&gt;2120 = MMCXX&lt;br /&gt;2121 = MMCXXI&lt;br /&gt;2122 = MMCXXII&lt;br /&gt;2123 = MMCXXIII&lt;br /&gt;2124 = MMCXXIV&lt;br /&gt;2125 = MMCXXV&lt;br /&gt;2126 = MMCXXVI&lt;br /&gt;2127 = MMCXXVII&lt;br /&gt;2128 = MMCXXVIII&lt;br /&gt;2129 = MMCXXIX&lt;br /&gt;2130 = MMCXXX&lt;br /&gt;2131 = MMCXXXI&lt;br /&gt;2132 = MMCXXXII&lt;br /&gt;2133 = MMCXXXIII&lt;br /&gt;2134 = MMCXXXIV&lt;br /&gt;2135 = MMCXXXV&lt;br /&gt;2136 = MMCXXXVI&lt;br /&gt;2137 = MMCXXXVII&lt;br /&gt;2138 = MMCXXXVIII&lt;br /&gt;2139 = MMCXXXIX&lt;br /&gt;2140 = MMCXL&lt;br /&gt;2141 = MMCXLI&lt;br /&gt;2142 = MMCXLII&lt;br /&gt;2143 = MMCXLIII&lt;br /&gt;2144 = MMCXLIV&lt;br /&gt;2145 = MMCXLV&lt;br /&gt;2146 = MMCXLVI&lt;br /&gt;2147 = MMCXLVII&lt;br /&gt;2148 = MMCXLVIII&lt;br /&gt;2149 = MMCXLIX&lt;br /&gt;2150 = MMCL&lt;br /&gt;2151 = MMCLI&lt;br /&gt;2152 = MMCLII&lt;br /&gt;2153 = MMCLIII&lt;br /&gt;2154 = MMCLIV&lt;br /&gt;2155 = MMCLV&lt;br /&gt;2156 = MMCLVI&lt;br /&gt;2157 = MMCLVII&lt;br /&gt;2158 = MMCLVIII&lt;br /&gt;2159 = MMCLIX&lt;br /&gt;2160 = MMCLX&lt;br /&gt;2161 = MMCLXI&lt;br /&gt;2162 = MMCLXII&lt;br /&gt;2163 = MMCLXIII&lt;br /&gt;2164 = MMCLXIV&lt;br /&gt;2165 = MMCLXV&lt;br /&gt;2166 = MMCLXVI&lt;br /&gt;2167 = MMCLXVII&lt;br /&gt;2168 = MMCLXVIII&lt;br /&gt;2169 = MMCLXIX&lt;br /&gt;2170 = MMCLXX&lt;br /&gt;2171 = MMCLXXI&lt;br /&gt;2172 = MMCLXXII&lt;br /&gt;2173 = MMCLXXIII&lt;br /&gt;2174 = MMCLXXIV&lt;br /&gt;2175 = MMCLXXV&lt;br /&gt;2176 = MMCLXXVI&lt;br /&gt;2177 = MMCLXXVII&lt;br /&gt;2178 = MMCLXXVIII&lt;br /&gt;2179 = MMCLXXIX&lt;br /&gt;2180 = MMCLXXX&lt;br /&gt;2181 = MMCLXXXI&lt;br /&gt;2182 = MMCLXXXII&lt;br /&gt;2183 = MMCLXXXIII&lt;br /&gt;2184 = MMCLXXXIV&lt;br /&gt;2185 = MMCLXXXV&lt;br /&gt;2186 = MMCLXXXVI&lt;br /&gt;2187 = MMCLXXXVII&lt;br /&gt;2188 = MMCLXXXVIII&lt;br /&gt;2189 = MMCLXXXIX&lt;br /&gt;2190 = MMCXC&lt;br /&gt;2191 = MMCXCI&lt;br /&gt;2192 = MMCXCII&lt;br /&gt;2193 = MMCXCIII&lt;br /&gt;2194 = MMCXCIV&lt;br /&gt;2195 = MMCXCV&lt;br /&gt;2196 = MMCXCVI&lt;br /&gt;2197 = MMCXCVII&lt;br /&gt;2198 = MMCXCVIII&lt;br /&gt;2199 = MMCXCIX&lt;br /&gt;2200 = MMCC&lt;br /&gt;2201 = MMCCI&lt;br /&gt;2202 = MMCCII&lt;br /&gt;2203 = MMCCIII&lt;br /&gt;2204 = MMCCIV&lt;br /&gt;2205 = MMCCV&lt;br /&gt;2206 = MMCCVI&lt;br /&gt;2207 = MMCCVII&lt;br /&gt;2208 = MMCCVIII&lt;br /&gt;2209 = MMCCIX&lt;br /&gt;2210 = MMCCX&lt;br /&gt;2211 = MMCCXI&lt;br /&gt;2212 = MMCCXII&lt;br /&gt;2213 = MMCCXIII&lt;br /&gt;2214 = MMCCXIV&lt;br /&gt;2215 = MMCCXV&lt;br /&gt;2216 = MMCCXVI&lt;br /&gt;2217 = MMCCXVII&lt;br /&gt;2218 = MMCCXVIII&lt;br /&gt;2219 = MMCCXIX&lt;br /&gt;2220 = MMCCXX&lt;br /&gt;2221 = MMCCXXI&lt;br /&gt;2222 = MMCCXXII&lt;br /&gt;2223 = MMCCXXIII&lt;br /&gt;2224 = MMCCXXIV&lt;br /&gt;2225 = MMCCXXV&lt;br /&gt;2226 = MMCCXXVI&lt;br /&gt;2227 = MMCCXXVII&lt;br /&gt;2228 = MMCCXXVIII&lt;br /&gt;2229 = MMCCXXIX&lt;br /&gt;2230 = MMCCXXX&lt;br /&gt;2231 = MMCCXXXI&lt;br /&gt;2232 = MMCCXXXII&lt;br /&gt;2233 = MMCCXXXIII&lt;br /&gt;2234 = MMCCXXXIV&lt;br /&gt;2235 = MMCCXXXV&lt;br /&gt;2236 = MMCCXXXVI&lt;br /&gt;2237 = MMCCXXXVII&lt;br /&gt;2238 = MMCCXXXVIII&lt;br /&gt;2239 = MMCCXXXIX&lt;br /&gt;2240 = MMCCXL&lt;br /&gt;2241 = MMCCXLI&lt;br /&gt;2242 = MMCCXLII&lt;br /&gt;2243 = MMCCXLIII&lt;br /&gt;2244 = MMCCXLIV&lt;br /&gt;2245 = MMCCXLV&lt;br /&gt;2246 = MMCCXLVI&lt;br /&gt;2247 = MMCCXLVII&lt;br /&gt;2248 = MMCCXLVIII&lt;br /&gt;2249 = MMCCXLIX&lt;br /&gt;2250 = MMCCL&lt;br /&gt;2251 = MMCCLI&lt;br /&gt;2252 = MMCCLII&lt;br /&gt;2253 = MMCCLIII&lt;br /&gt;2254 = MMCCLIV&lt;br /&gt;2255 = MMCCLV&lt;br /&gt;2256 = MMCCLVI&lt;br /&gt;2257 = MMCCLVII&lt;br /&gt;2258 = MMCCLVIII&lt;br /&gt;2259 = MMCCLIX&lt;br /&gt;2260 = MMCCLX&lt;br /&gt;2261 = MMCCLXI&lt;br /&gt;2262 = MMCCLXII&lt;br /&gt;2263 = MMCCLXIII&lt;br /&gt;2264 = MMCCLXIV&lt;br /&gt;2265 = MMCCLXV&lt;br /&gt;2266 = MMCCLXVI&lt;br /&gt;2267 = MMCCLXVII&lt;br /&gt;2268 = MMCCLXVIII&lt;br /&gt;2269 = MMCCLXIX&lt;br /&gt;2270 = MMCCLXX&lt;br /&gt;2271 = MMCCLXXI&lt;br /&gt;2272 = MMCCLXXII&lt;br /&gt;2273 = MMCCLXXIII&lt;br /&gt;2274 = MMCCLXXIV&lt;br /&gt;2275 = MMCCLXXV&lt;br /&gt;2276 = MMCCLXXVI&lt;br /&gt;2277 = MMCCLXXVII&lt;br /&gt;2278 = MMCCLXXVIII&lt;br /&gt;2279 = MMCCLXXIX&lt;br /&gt;2280 = MMCCLXXX&lt;br /&gt;2281 = MMCCLXXXI&lt;br /&gt;2282 = MMCCLXXXII&lt;br /&gt;2283 = MMCCLXXXIII&lt;br /&gt;2284 = MMCCLXXXIV&lt;br /&gt;2285 = MMCCLXXXV&lt;br /&gt;2286 = MMCCLXXXVI&lt;br /&gt;2287 = MMCCLXXXVII&lt;br /&gt;2288 = MMCCLXXXVIII&lt;br /&gt;2289 = MMCCLXXXIX&lt;br /&gt;2290 = MMCCXC&lt;br /&gt;2291 = MMCCXCI&lt;br /&gt;2292 = MMCCXCII&lt;br /&gt;2293 = MMCCXCIII&lt;br /&gt;2294 = MMCCXCIV&lt;br /&gt;2295 = MMCCXCV&lt;br /&gt;2296 = MMCCXCVI&lt;br /&gt;2297 = MMCCXCVII&lt;br /&gt;2298 = MMCCXCVIII&lt;br /&gt;2299 = MMCCXCIX&lt;br /&gt;2300 = MMCCC&lt;br /&gt;2301 = MMCCCI&lt;br /&gt;2302 = MMCCCII&lt;br /&gt;2303 = MMCCCIII&lt;br /&gt;2304 = MMCCCIV&lt;br /&gt;2305 = MMCCCV&lt;br /&gt;2306 = MMCCCVI&lt;br /&gt;2307 = MMCCCVII&lt;br /&gt;2308 = MMCCCVIII&lt;br /&gt;2309 = MMCCCIX&lt;br /&gt;2310 = MMCCCX&lt;br /&gt;2311 = MMCCCXI&lt;br /&gt;2312 = MMCCCXII&lt;br /&gt;2313 = MMCCCXIII&lt;br /&gt;2314 = MMCCCXIV&lt;br /&gt;2315 = MMCCCXV&lt;br /&gt;2316 = MMCCCXVI&lt;br /&gt;2317 = MMCCCXVII&lt;br /&gt;2318 = MMCCCXVIII&lt;br /&gt;2319 = MMCCCXIX&lt;br /&gt;2320 = MMCCCXX&lt;br /&gt;2321 = MMCCCXXI&lt;br /&gt;2322 = MMCCCXXII&lt;br /&gt;2323 = MMCCCXXIII&lt;br /&gt;2324 = MMCCCXXIV&lt;br /&gt;2325 = MMCCCXXV&lt;br /&gt;2326 = MMCCCXXVI&lt;br /&gt;2327 = MMCCCXXVII&lt;br /&gt;2328 = MMCCCXXVIII&lt;br /&gt;2329 = MMCCCXXIX&lt;br /&gt;2330 = MMCCCXXX&lt;br /&gt;2331 = MMCCCXXXI&lt;br /&gt;2332 = MMCCCXXXII&lt;br /&gt;2333 = MMCCCXXXIII&lt;br /&gt;2334 = MMCCCXXXIV&lt;br /&gt;2335 = MMCCCXXXV&lt;br /&gt;2336 = MMCCCXXXVI&lt;br /&gt;2337 = MMCCCXXXVII&lt;br /&gt;2338 = MMCCCXXXVIII&lt;br /&gt;2339 = MMCCCXXXIX&lt;br /&gt;2340 = MMCCCXL&lt;br /&gt;2341 = MMCCCXLI&lt;br /&gt;2342 = MMCCCXLII&lt;br /&gt;2343 = MMCCCXLIII&lt;br /&gt;2344 = MMCCCXLIV&lt;br /&gt;2345 = MMCCCXLV&lt;br /&gt;2346 = MMCCCXLVI&lt;br /&gt;2347 = MMCCCXLVII&lt;br /&gt;2348 = MMCCCXLVIII&lt;br /&gt;2349 = MMCCCXLIX&lt;br /&gt;2350 = MMCCCL&lt;br /&gt;2351 = MMCCCLI&lt;br /&gt;2352 = MMCCCLII&lt;br /&gt;2353 = MMCCCLIII&lt;br /&gt;2354 = MMCCCLIV&lt;br /&gt;2355 = MMCCCLV&lt;br /&gt;2356 = MMCCCLVI&lt;br /&gt;2357 = MMCCCLVII&lt;br /&gt;2358 = MMCCCLVIII&lt;br /&gt;2359 = MMCCCLIX&lt;br /&gt;2360 = MMCCCLX&lt;br /&gt;2361 = MMCCCLXI&lt;br /&gt;2362 = MMCCCLXII&lt;br /&gt;2363 = MMCCCLXIII&lt;br /&gt;2364 = MMCCCLXIV&lt;br /&gt;2365 = MMCCCLXV&lt;br /&gt;2366 = MMCCCLXVI&lt;br /&gt;2367 = MMCCCLXVII&lt;br /&gt;2368 = MMCCCLXVIII&lt;br /&gt;2369 = MMCCCLXIX&lt;br /&gt;2370 = MMCCCLXX&lt;br /&gt;2371 = MMCCCLXXI&lt;br /&gt;2372 = MMCCCLXXII&lt;br /&gt;2373 = MMCCCLXXIII&lt;br /&gt;2374 = MMCCCLXXIV&lt;br /&gt;2375 = MMCCCLXXV&lt;br /&gt;2376 = MMCCCLXXVI&lt;br /&gt;2377 = MMCCCLXXVII&lt;br /&gt;2378 = MMCCCLXXVIII&lt;br /&gt;2379 = MMCCCLXXIX&lt;br /&gt;2380 = MMCCCLXXX&lt;br /&gt;2381 = MMCCCLXXXI&lt;br /&gt;2382 = MMCCCLXXXII&lt;br /&gt;2383 = MMCCCLXXXIII&lt;br /&gt;2384 = MMCCCLXXXIV&lt;br /&gt;2385 = MMCCCLXXXV&lt;br /&gt;2386 = MMCCCLXXXVI&lt;br /&gt;2387 = MMCCCLXXXVII&lt;br /&gt;2388 = MMCCCLXXXVIII&lt;br /&gt;2389 = MMCCCLXXXIX&lt;br /&gt;2390 = MMCCCXC&lt;br /&gt;2391 = MMCCCXCI&lt;br /&gt;2392 = MMCCCXCII&lt;br /&gt;2393 = MMCCCXCIII&lt;br /&gt;2394 = MMCCCXCIV&lt;br /&gt;2395 = MMCCCXCV&lt;br /&gt;2396 = MMCCCXCVI&lt;br /&gt;2397 = MMCCCXCVII&lt;br /&gt;2398 = MMCCCXCVIII&lt;br /&gt;2399 = MMCCCXCIX&lt;br /&gt;2400 = MMCD&lt;br /&gt;2401 = MMCDI&lt;br /&gt;2402 = MMCDII&lt;br /&gt;2403 = MMCDIII&lt;br /&gt;2404 = MMCDIV&lt;br /&gt;2405 = MMCDV&lt;br /&gt;2406 = MMCDVI&lt;br /&gt;2407 = MMCDVII&lt;br /&gt;2408 = MMCDVIII&lt;br /&gt;2409 = MMCDIX&lt;br /&gt;2410 = MMCDX&lt;br /&gt;2411 = MMCDXI&lt;br /&gt;2412 = MMCDXII&lt;br /&gt;2413 = MMCDXIII&lt;br /&gt;2414 = MMCDXIV&lt;br /&gt;2415 = MMCDXV&lt;br /&gt;2416 = MMCDXVI&lt;br /&gt;2417 = MMCDXVII&lt;br /&gt;2418 = MMCDXVIII&lt;br /&gt;2419 = MMCDXIX&lt;br /&gt;2420 = MMCDXX&lt;br /&gt;2421 = MMCDXXI&lt;br /&gt;2422 = MMCDXXII&lt;br /&gt;2423 = MMCDXXIII&lt;br /&gt;2424 = MMCDXXIV&lt;br /&gt;2425 = MMCDXXV&lt;br /&gt;2426 = MMCDXXVI&lt;br /&gt;2427 = MMCDXXVII&lt;br /&gt;2428 = MMCDXXVIII&lt;br /&gt;2429 = MMCDXXIX&lt;br /&gt;2430 = MMCDXXX&lt;br /&gt;2431 = MMCDXXXI&lt;br /&gt;2432 = MMCDXXXII&lt;br /&gt;2433 = MMCDXXXIII&lt;br /&gt;2434 = MMCDXXXIV&lt;br /&gt;2435 = MMCDXXXV&lt;br /&gt;2436 = MMCDXXXVI&lt;br /&gt;2437 = MMCDXXXVII&lt;br /&gt;2438 = MMCDXXXVIII&lt;br /&gt;2439 = MMCDXXXIX&lt;br /&gt;2440 = MMCDXL&lt;br /&gt;2441 = MMCDXLI&lt;br /&gt;2442 = MMCDXLII&lt;br /&gt;2443 = MMCDXLIII&lt;br /&gt;2444 = MMCDXLIV&lt;br /&gt;2445 = MMCDXLV&lt;br /&gt;2446 = MMCDXLVI&lt;br /&gt;2447 = MMCDXLVII&lt;br /&gt;2448 = MMCDXLVIII&lt;br /&gt;2449 = MMCDXLIX&lt;br /&gt;2450 = MMCDL&lt;br /&gt;2451 = MMCDLI&lt;br /&gt;2452 = MMCDLII&lt;br /&gt;2453 = MMCDLIII&lt;br /&gt;2454 = MMCDLIV&lt;br /&gt;2455 = MMCDLV&lt;br /&gt;2456 = MMCDLVI&lt;br /&gt;2457 = MMCDLVII&lt;br /&gt;2458 = MMCDLVIII&lt;br /&gt;2459 = MMCDLIX&lt;br /&gt;2460 = MMCDLX&lt;br /&gt;2461 = MMCDLXI&lt;br /&gt;2462 = MMCDLXII&lt;br /&gt;2463 = MMCDLXIII&lt;br /&gt;2464 = MMCDLXIV&lt;br /&gt;2465 = MMCDLXV&lt;br /&gt;2466 = MMCDLXVI&lt;br /&gt;2467 = MMCDLXVII&lt;br /&gt;2468 = MMCDLXVIII&lt;br /&gt;2469 = MMCDLXIX&lt;br /&gt;2470 = MMCDLXX&lt;br /&gt;2471 = MMCDLXXI&lt;br /&gt;2472 = MMCDLXXII&lt;br /&gt;2473 = MMCDLXXIII&lt;br /&gt;2474 = MMCDLXXIV&lt;br /&gt;2475 = MMCDLXXV&lt;br /&gt;2476 = MMCDLXXVI&lt;br /&gt;2477 = MMCDLXXVII&lt;br /&gt;2478 = MMCDLXXVIII&lt;br /&gt;2479 = MMCDLXXIX&lt;br /&gt;2480 = MMCDLXXX&lt;br /&gt;2481 = MMCDLXXXI&lt;br /&gt;2482 = MMCDLXXXII&lt;br /&gt;2483 = MMCDLXXXIII&lt;br /&gt;2484 = MMCDLXXXIV&lt;br /&gt;2485 = MMCDLXXXV&lt;br /&gt;2486 = MMCDLXXXVI&lt;br /&gt;2487 = MMCDLXXXVII&lt;br /&gt;2488 = MMCDLXXXVIII&lt;br /&gt;2489 = MMCDLXXXIX&lt;br /&gt;2490 = MMCDXC&lt;br /&gt;2491 = MMCDXCI&lt;br /&gt;2492 = MMCDXCII&lt;br /&gt;2493 = MMCDXCIII&lt;br /&gt;2494 = MMCDXCIV&lt;br /&gt;2495 = MMCDXCV&lt;br /&gt;2496 = MMCDXCVI&lt;br /&gt;2497 = MMCDXCVII&lt;br /&gt;2498 = MMCDXCVIII&lt;br /&gt;2499 = MMCDXCIX&lt;br /&gt;2500 = MMD&lt;br /&gt;2501 = MMDI&lt;br /&gt;2502 = MMDII&lt;br /&gt;2503 = MMDIII&lt;br /&gt;2504 = MMDIV&lt;br /&gt;2505 = MMDV&lt;br /&gt;2506 = MMDVI&lt;br /&gt;2507 = MMDVII&lt;br /&gt;2508 = MMDVIII&lt;br /&gt;2509 = MMDIX&lt;br /&gt;2510 = MMDX&lt;br /&gt;2511 = MMDXI&lt;br /&gt;2512 = MMDXII&lt;br /&gt;2513 = MMDXIII&lt;br /&gt;2514 = MMDXIV&lt;br /&gt;2515 = MMDXV&lt;br /&gt;2516 = MMDXVI&lt;br /&gt;2517 = MMDXVII&lt;br /&gt;2518 = MMDXVIII&lt;br /&gt;2519 = MMDXIX&lt;br /&gt;2520 = MMDXX&lt;br /&gt;2521 = MMDXXI&lt;br /&gt;2522 = MMDXXII&lt;br /&gt;2523 = MMDXXIII&lt;br /&gt;2524 = MMDXXIV&lt;br /&gt;2525 = MMDXXV&lt;br /&gt;2526 = MMDXXVI&lt;br /&gt;2527 = MMDXXVII&lt;br /&gt;2528 = MMDXXVIII&lt;br /&gt;2529 = MMDXXIX&lt;br /&gt;2530 = MMDXXX&lt;br /&gt;2531 = MMDXXXI&lt;br /&gt;2532 = MMDXXXII&lt;br /&gt;2533 = MMDXXXIII&lt;br /&gt;2534 = MMDXXXIV&lt;br /&gt;2535 = MMDXXXV&lt;br /&gt;2536 = MMDXXXVI&lt;br /&gt;2537 = MMDXXXVII&lt;br /&gt;2538 = MMDXXXVIII&lt;br /&gt;2539 = MMDXXXIX&lt;br /&gt;2540 = MMDXL&lt;br /&gt;2541 = MMDXLI&lt;br /&gt;2542 = MMDXLII&lt;br /&gt;2543 = MMDXLIII&lt;br /&gt;2544 = MMDXLIV&lt;br /&gt;2545 = MMDXLV&lt;br /&gt;2546 = MMDXLVI&lt;br /&gt;2547 = MMDXLVII&lt;br /&gt;2548 = MMDXLVIII&lt;br /&gt;2549 = MMDXLIX&lt;br /&gt;2550 = MMDL&lt;br /&gt;2551 = MMDLI&lt;br /&gt;2552 = MMDLII&lt;br /&gt;2553 = MMDLIII&lt;br /&gt;2554 = MMDLIV&lt;br /&gt;2555 = MMDLV&lt;br /&gt;2556 = MMDLVI&lt;br /&gt;2557 = MMDLVII&lt;br /&gt;2558 = MMDLVIII&lt;br /&gt;2559 = MMDLIX&lt;br /&gt;2560 = MMDLX&lt;br /&gt;2561 = MMDLXI&lt;br /&gt;2562 = MMDLXII&lt;br /&gt;2563 = MMDLXIII&lt;br /&gt;2564 = MMDLXIV&lt;br /&gt;2565 = MMDLXV&lt;br /&gt;2566 = MMDLXVI&lt;br /&gt;2567 = MMDLXVII&lt;br /&gt;2568 = MMDLXVIII&lt;br /&gt;2569 = MMDLXIX&lt;br /&gt;2570 = MMDLXX&lt;br /&gt;2571 = MMDLXXI&lt;br /&gt;2572 = MMDLXXII&lt;br /&gt;2573 = MMDLXXIII&lt;br /&gt;2574 = MMDLXXIV&lt;br /&gt;2575 = MMDLXXV&lt;br /&gt;2576 = MMDLXXVI&lt;br /&gt;2577 = MMDLXXVII&lt;br /&gt;2578 = MMDLXXVIII&lt;br /&gt;2579 = MMDLXXIX&lt;br /&gt;2580 = MMDLXXX&lt;br /&gt;2581 = MMDLXXXI&lt;br /&gt;2582 = MMDLXXXII&lt;br /&gt;2583 = MMDLXXXIII&lt;br /&gt;2584 = MMDLXXXIV&lt;br /&gt;2585 = MMDLXXXV&lt;br /&gt;2586 = MMDLXXXVI&lt;br /&gt;2587 = MMDLXXXVII&lt;br /&gt;2588 = MMDLXXXVIII&lt;br /&gt;2589 = MMDLXXXIX&lt;br /&gt;2590 = MMDXC&lt;br /&gt;2591 = MMDXCI&lt;br /&gt;2592 = MMDXCII&lt;br /&gt;2593 = MMDXCIII&lt;br /&gt;2594 = MMDXCIV&lt;br /&gt;2595 = MMDXCV&lt;br /&gt;2596 = MMDXCVI&lt;br /&gt;2597 = MMDXCVII&lt;br /&gt;2598 = MMDXCVIII&lt;br /&gt;2599 = MMDXCIX&lt;br /&gt;2600 = MMDC&lt;br /&gt;2601 = MMDCI&lt;br /&gt;2602 = MMDCII&lt;br /&gt;2603 = MMDCIII&lt;br /&gt;2604 = MMDCIV&lt;br /&gt;2605 = MMDCV&lt;br /&gt;2606 = MMDCVI&lt;br /&gt;2607 = MMDCVII&lt;br /&gt;2608 = MMDCVIII&lt;br /&gt;2609 = MMDCIX&lt;br /&gt;2610 = MMDCX&lt;br /&gt;2611 = MMDCXI&lt;br /&gt;2612 = MMDCXII&lt;br /&gt;2613 = MMDCXIII&lt;br /&gt;2614 = MMDCXIV&lt;br /&gt;2615 = MMDCXV&lt;br /&gt;2616 = MMDCXVI&lt;br /&gt;2617 = MMDCXVII&lt;br /&gt;2618 = MMDCXVIII&lt;br /&gt;2619 = MMDCXIX&lt;br /&gt;2620 = MMDCXX&lt;br /&gt;2621 = MMDCXXI&lt;br /&gt;2622 = MMDCXXII&lt;br /&gt;2623 = MMDCXXIII&lt;br /&gt;2624 = MMDCXXIV&lt;br /&gt;2625 = MMDCXXV&lt;br /&gt;2626 = MMDCXXVI&lt;br /&gt;2627 = MMDCXXVII&lt;br /&gt;2628 = MMDCXXVIII&lt;br /&gt;2629 = MMDCXXIX&lt;br /&gt;2630 = MMDCXXX&lt;br /&gt;2631 = MMDCXXXI&lt;br /&gt;2632 = MMDCXXXII&lt;br /&gt;2633 = MMDCXXXIII&lt;br /&gt;2634 = MMDCXXXIV&lt;br /&gt;2635 = MMDCXXXV&lt;br /&gt;2636 = MMDCXXXVI&lt;br /&gt;2637 = MMDCXXXVII&lt;br /&gt;2638 = MMDCXXXVIII&lt;br /&gt;2639 = MMDCXXXIX&lt;br /&gt;2640 = MMDCXL&lt;br /&gt;2641 = MMDCXLI&lt;br /&gt;2642 = MMDCXLII&lt;br /&gt;2643 = MMDCXLIII&lt;br /&gt;2644 = MMDCXLIV&lt;br /&gt;2645 = MMDCXLV&lt;br /&gt;2646 = MMDCXLVI&lt;br /&gt;2647 = MMDCXLVII&lt;br /&gt;2648 = MMDCXLVIII&lt;br /&gt;2649 = MMDCXLIX&lt;br /&gt;2650 = MMDCL&lt;br /&gt;2651 = MMDCLI&lt;br /&gt;2652 = MMDCLII&lt;br /&gt;2653 = MMDCLIII&lt;br /&gt;2654 = MMDCLIV&lt;br /&gt;2655 = MMDCLV&lt;br /&gt;2656 = MMDCLVI&lt;br /&gt;2657 = MMDCLVII&lt;br /&gt;2658 = MMDCLVIII&lt;br /&gt;2659 = MMDCLIX&lt;br /&gt;2660 = MMDCLX&lt;br /&gt;2661 = MMDCLXI&lt;br /&gt;2662 = MMDCLXII&lt;br /&gt;2663 = MMDCLXIII&lt;br /&gt;2664 = MMDCLXIV&lt;br /&gt;2665 = MMDCLXV&lt;br /&gt;2666 = MMDCLXVI&lt;br /&gt;2667 = MMDCLXVII&lt;br /&gt;2668 = MMDCLXVIII&lt;br /&gt;2669 = MMDCLXIX&lt;br /&gt;2670 = MMDCLXX&lt;br /&gt;2671 = MMDCLXXI&lt;br /&gt;2672 = MMDCLXXII&lt;br /&gt;2673 = MMDCLXXIII&lt;br /&gt;2674 = MMDCLXXIV&lt;br /&gt;2675 = MMDCLXXV&lt;br /&gt;2676 = MMDCLXXVI&lt;br /&gt;2677 = MMDCLXXVII&lt;br /&gt;2678 = MMDCLXXVIII&lt;br /&gt;2679 = MMDCLXXIX&lt;br /&gt;2680 = MMDCLXXX&lt;br /&gt;2681 = MMDCLXXXI&lt;br /&gt;2682 = MMDCLXXXII&lt;br /&gt;2683 = MMDCLXXXIII&lt;br /&gt;2684 = MMDCLXXXIV&lt;br /&gt;2685 = MMDCLXXXV&lt;br /&gt;2686 = MMDCLXXXVI&lt;br /&gt;2687 = MMDCLXXXVII&lt;br /&gt;2688 = MMDCLXXXVIII&lt;br /&gt;2689 = MMDCLXXXIX&lt;br /&gt;2690 = MMDCXC&lt;br /&gt;2691 = MMDCXCI&lt;br /&gt;2692 = MMDCXCII&lt;br /&gt;2693 = MMDCXCIII&lt;br /&gt;2694 = MMDCXCIV&lt;br /&gt;2695 = MMDCXCV&lt;br /&gt;2696 = MMDCXCVI&lt;br /&gt;2697 = MMDCXCVII&lt;br /&gt;2698 = MMDCXCVIII&lt;br /&gt;2699 = MMDCXCIX&lt;br /&gt;2700 = MMDCC&lt;br /&gt;2701 = MMDCCI&lt;br /&gt;2702 = MMDCCII&lt;br /&gt;2703 = MMDCCIII&lt;br /&gt;2704 = MMDCCIV&lt;br /&gt;2705 = MMDCCV&lt;br /&gt;2706 = MMDCCVI&lt;br /&gt;2707 = MMDCCVII&lt;br /&gt;2708 = MMDCCVIII&lt;br /&gt;2709 = MMDCCIX&lt;br /&gt;2710 = MMDCCX&lt;br /&gt;2711 = MMDCCXI&lt;br /&gt;2712 = MMDCCXII&lt;br /&gt;2713 = MMDCCXIII&lt;br /&gt;2714 = MMDCCXIV&lt;br /&gt;2715 = MMDCCXV&lt;br /&gt;2716 = MMDCCXVI&lt;br /&gt;2717 = MMDCCXVII&lt;br /&gt;2718 = MMDCCXVIII&lt;br /&gt;2719 = MMDCCXIX&lt;br /&gt;2720 = MMDCCXX&lt;br /&gt;2721 = MMDCCXXI&lt;br /&gt;2722 = MMDCCXXII&lt;br /&gt;2723 = MMDCCXXIII&lt;br /&gt;2724 = MMDCCXXIV&lt;br /&gt;2725 = MMDCCXXV&lt;br /&gt;2726 = MMDCCXXVI&lt;br /&gt;2727 = MMDCCXXVII&lt;br /&gt;2728 = MMDCCXXVIII&lt;br /&gt;2729 = MMDCCXXIX&lt;br /&gt;2730 = MMDCCXXX&lt;br /&gt;2731 = MMDCCXXXI&lt;br /&gt;2732 = MMDCCXXXII&lt;br /&gt;2733 = MMDCCXXXIII&lt;br /&gt;2734 = MMDCCXXXIV&lt;br /&gt;2735 = MMDCCXXXV&lt;br /&gt;2736 = MMDCCXXXVI&lt;br /&gt;2737 = MMDCCXXXVII&lt;br /&gt;2738 = MMDCCXXXVIII&lt;br /&gt;2739 = MMDCCXXXIX&lt;br /&gt;2740 = MMDCCXL&lt;br /&gt;2741 = MMDCCXLI&lt;br /&gt;2742 = MMDCCXLII&lt;br /&gt;2743 = MMDCCXLIII&lt;br /&gt;2744 = MMDCCXLIV&lt;br /&gt;2745 = MMDCCXLV&lt;br /&gt;2746 = MMDCCXLVI&lt;br /&gt;2747 = MMDCCXLVII&lt;br /&gt;2748 = MMDCCXLVIII&lt;br /&gt;2749 = MMDCCXLIX&lt;br /&gt;2750 = MMDCCL&lt;br /&gt;2751 = MMDCCLI&lt;br /&gt;2752 = MMDCCLII&lt;br /&gt;2753 = MMDCCLIII&lt;br /&gt;2754 = MMDCCLIV&lt;br /&gt;2755 = MMDCCLV&lt;br /&gt;2756 = MMDCCLVI&lt;br /&gt;2757 = MMDCCLVII&lt;br /&gt;2758 = MMDCCLVIII&lt;br /&gt;2759 = MMDCCLIX&lt;br /&gt;2760 = MMDCCLX&lt;br /&gt;2761 = MMDCCLXI&lt;br /&gt;2762 = MMDCCLXII&lt;br /&gt;2763 = MMDCCLXIII&lt;br /&gt;2764 = MMDCCLXIV&lt;br /&gt;2765 = MMDCCLXV&lt;br /&gt;2766 = MMDCCLXVI&lt;br /&gt;2767 = MMDCCLXVII&lt;br /&gt;2768 = MMDCCLXVIII&lt;br /&gt;2769 = MMDCCLXIX&lt;br /&gt;2770 = MMDCCLXX&lt;br /&gt;2771 = MMDCCLXXI&lt;br /&gt;2772 = MMDCCLXXII&lt;br /&gt;2773 = MMDCCLXXIII&lt;br /&gt;2774 = MMDCCLXXIV&lt;br /&gt;2775 = MMDCCLXXV&lt;br /&gt;2776 = MMDCCLXXVI&lt;br /&gt;2777 = MMDCCLXXVII&lt;br /&gt;2778 = MMDCCLXXVIII&lt;br /&gt;2779 = MMDCCLXXIX&lt;br /&gt;2780 = MMDCCLXXX&lt;br /&gt;2781 = MMDCCLXXXI&lt;br /&gt;2782 = MMDCCLXXXII&lt;br /&gt;2783 = MMDCCLXXXIII&lt;br /&gt;2784 = MMDCCLXXXIV&lt;br /&gt;2785 = MMDCCLXXXV&lt;br /&gt;2786 = MMDCCLXXXVI&lt;br /&gt;2787 = MMDCCLXXXVII&lt;br /&gt;2788 = MMDCCLXXXVIII&lt;br /&gt;2789 = MMDCCLXXXIX&lt;br /&gt;2790 = MMDCCXC&lt;br /&gt;2791 = MMDCCXCI&lt;br /&gt;2792 = MMDCCXCII&lt;br /&gt;2793 = MMDCCXCIII&lt;br /&gt;2794 = MMDCCXCIV&lt;br /&gt;2795 = MMDCCXCV&lt;br /&gt;2796 = MMDCCXCVI&lt;br /&gt;2797 = MMDCCXCVII&lt;br /&gt;2798 = MMDCCXCVIII&lt;br /&gt;2799 = MMDCCXCIX&lt;br /&gt;2800 = MMDCCC&lt;br /&gt;2801 = MMDCCCI&lt;br /&gt;2802 = MMDCCCII&lt;br /&gt;2803 = MMDCCCIII&lt;br /&gt;2804 = MMDCCCIV&lt;br /&gt;2805 = MMDCCCV&lt;br /&gt;2806 = MMDCCCVI&lt;br /&gt;2807 = MMDCCCVII&lt;br /&gt;2808 = MMDCCCVIII&lt;br /&gt;2809 = MMDCCCIX&lt;br /&gt;2810 = MMDCCCX&lt;br /&gt;2811 = MMDCCCXI&lt;br /&gt;2812 = MMDCCCXII&lt;br /&gt;2813 = MMDCCCXIII&lt;br /&gt;2814 = MMDCCCXIV&lt;br /&gt;2815 = MMDCCCXV&lt;br /&gt;2816 = MMDCCCXVI&lt;br /&gt;2817 = MMDCCCXVII&lt;br /&gt;2818 = MMDCCCXVIII&lt;br /&gt;2819 = MMDCCCXIX&lt;br /&gt;2820 = MMDCCCXX&lt;br /&gt;2821 = MMDCCCXXI&lt;br /&gt;2822 = MMDCCCXXII&lt;br /&gt;2823 = MMDCCCXXIII&lt;br /&gt;2824 = MMDCCCXXIV&lt;br /&gt;2825 = MMDCCCXXV&lt;br /&gt;2826 = MMDCCCXXVI&lt;br /&gt;2827 = MMDCCCXXVII&lt;br /&gt;2828 = MMDCCCXXVIII&lt;br /&gt;2829 = MMDCCCXXIX&lt;br /&gt;2830 = MMDCCCXXX&lt;br /&gt;2831 = MMDCCCXXXI&lt;br /&gt;2832 = MMDCCCXXXII&lt;br /&gt;2833 = MMDCCCXXXIII&lt;br /&gt;2834 = MMDCCCXXXIV&lt;br /&gt;2835 = MMDCCCXXXV&lt;br /&gt;2836 = MMDCCCXXXVI&lt;br /&gt;2837 = MMDCCCXXXVII&lt;br /&gt;2838 = MMDCCCXXXVIII&lt;br /&gt;2839 = MMDCCCXXXIX&lt;br /&gt;2840 = MMDCCCXL&lt;br /&gt;2841 = MMDCCCXLI&lt;br /&gt;2842 = MMDCCCXLII&lt;br /&gt;2843 = MMDCCCXLIII&lt;br /&gt;2844 = MMDCCCXLIV&lt;br /&gt;2845 = MMDCCCXLV&lt;br /&gt;2846 = MMDCCCXLVI&lt;br /&gt;2847 = MMDCCCXLVII&lt;br /&gt;2848 = MMDCCCXLVIII&lt;br /&gt;2849 = MMDCCCXLIX&lt;br /&gt;2850 = MMDCCCL&lt;br /&gt;2851 = MMDCCCLI&lt;br /&gt;2852 = MMDCCCLII&lt;br /&gt;2853 = MMDCCCLIII&lt;br /&gt;2854 = MMDCCCLIV&lt;br /&gt;2855 = MMDCCCLV&lt;br /&gt;2856 = MMDCCCLVI&lt;br /&gt;2857 = MMDCCCLVII&lt;br /&gt;2858 = MMDCCCLVIII&lt;br /&gt;2859 = MMDCCCLIX&lt;br /&gt;2860 = MMDCCCLX&lt;br /&gt;2861 = MMDCCCLXI&lt;br /&gt;2862 = MMDCCCLXII&lt;br /&gt;2863 = MMDCCCLXIII&lt;br /&gt;2864 = MMDCCCLXIV&lt;br /&gt;2865 = MMDCCCLXV&lt;br /&gt;2866 = MMDCCCLXVI&lt;br /&gt;2867 = MMDCCCLXVII&lt;br /&gt;2868 = MMDCCCLXVIII&lt;br /&gt;2869 = MMDCCCLXIX&lt;br /&gt;2870 = MMDCCCLXX&lt;br /&gt;2871 = MMDCCCLXXI&lt;br /&gt;2872 = MMDCCCLXXII&lt;br /&gt;2873 = MMDCCCLXXIII&lt;br /&gt;2874 = MMDCCCLXXIV&lt;br /&gt;2875 = MMDCCCLXXV&lt;br /&gt;2876 = MMDCCCLXXVI&lt;br /&gt;2877 = MMDCCCLXXVII&lt;br /&gt;2878 = MMDCCCLXXVIII&lt;br /&gt;2879 = MMDCCCLXXIX&lt;br /&gt;2880 = MMDCCCLXXX&lt;br /&gt;2881 = MMDCCCLXXXI&lt;br /&gt;2882 = MMDCCCLXXXII&lt;br /&gt;2883 = MMDCCCLXXXIII&lt;br /&gt;2884 = MMDCCCLXXXIV&lt;br /&gt;2885 = MMDCCCLXXXV&lt;br /&gt;2886 = MMDCCCLXXXVI&lt;br /&gt;2887 = MMDCCCLXXXVII&lt;br /&gt;2888 = MMDCCCLXXXVIII&lt;br /&gt;2889 = MMDCCCLXXXIX&lt;br /&gt;2890 = MMDCCCXC&lt;br /&gt;2891 = MMDCCCXCI&lt;br /&gt;2892 = MMDCCCXCII&lt;br /&gt;2893 = MMDCCCXCIII&lt;br /&gt;2894 = MMDCCCXCIV&lt;br /&gt;2895 = MMDCCCXCV&lt;br /&gt;2896 = MMDCCCXCVI&lt;br /&gt;2897 = MMDCCCXCVII&lt;br /&gt;2898 = MMDCCCXCVIII&lt;br /&gt;2899 = MMDCCCXCIX&lt;br /&gt;2900 = MMCM&lt;br /&gt;2901 = MMCMI&lt;br /&gt;2902 = MMCMII&lt;br /&gt;2903 = MMCMIII&lt;br /&gt;2904 = MMCMIV&lt;br /&gt;2905 = MMCMV&lt;br /&gt;2906 = MMCMVI&lt;br /&gt;2907 = MMCMVII&lt;br /&gt;2908 = MMCMVIII&lt;br /&gt;2909 = MMCMIX&lt;br /&gt;2910 = MMCMX&lt;br /&gt;2911 = MMCMXI&lt;br /&gt;2912 = MMCMXII&lt;br /&gt;2913 = MMCMXIII&lt;br /&gt;2914 = MMCMXIV&lt;br /&gt;2915 = MMCMXV&lt;br /&gt;2916 = MMCMXVI&lt;br /&gt;2917 = MMCMXVII&lt;br /&gt;2918 = MMCMXVIII&lt;br /&gt;2919 = MMCMXIX&lt;br /&gt;2920 = MMCMXX&lt;br /&gt;2921 = MMCMXXI&lt;br /&gt;2922 = MMCMXXII&lt;br /&gt;2923 = MMCMXXIII&lt;br /&gt;2924 = MMCMXXIV&lt;br /&gt;2925 = MMCMXXV&lt;br /&gt;2926 = MMCMXXVI&lt;br /&gt;2927 = MMCMXXVII&lt;br /&gt;2928 = MMCMXXVIII&lt;br /&gt;2929 = MMCMXXIX&lt;br /&gt;2930 = MMCMXXX&lt;br /&gt;2931 = MMCMXXXI&lt;br /&gt;2932 = MMCMXXXII&lt;br /&gt;2933 = MMCMXXXIII&lt;br /&gt;2934 = MMCMXXXIV&lt;br /&gt;2935 = MMCMXXXV&lt;br /&gt;2936 = MMCMXXXVI&lt;br /&gt;2937 = MMCMXXXVII&lt;br /&gt;2938 = MMCMXXXVIII&lt;br /&gt;2939 = MMCMXXXIX&lt;br /&gt;2940 = MMCMXL&lt;br /&gt;2941 = MMCMXLI&lt;br /&gt;2942 = MMCMXLII&lt;br /&gt;2943 = MMCMXLIII&lt;br /&gt;2944 = MMCMXLIV&lt;br /&gt;2945 = MMCMXLV&lt;br /&gt;2946 = MMCMXLVI&lt;br /&gt;2947 = MMCMXLVII&lt;br /&gt;2948 = MMCMXLVIII&lt;br /&gt;2949 = MMCMXLIX&lt;br /&gt;2950 = MMCML&lt;br /&gt;2951 = MMCMLI&lt;br /&gt;2952 = MMCMLII&lt;br /&gt;2953 = MMCMLIII&lt;br /&gt;2954 = MMCMLIV&lt;br /&gt;2955 = MMCMLV&lt;br /&gt;2956 = MMCMLVI&lt;br /&gt;2957 = MMCMLVII&lt;br /&gt;2958 = MMCMLVIII&lt;br /&gt;2959 = MMCMLIX&lt;br /&gt;2960 = MMCMLX&lt;br /&gt;2961 = MMCMLXI&lt;br /&gt;2962 = MMCMLXII&lt;br /&gt;2963 = MMCMLXIII&lt;br /&gt;2964 = MMCMLXIV&lt;br /&gt;2965 = MMCMLXV&lt;br /&gt;2966 = MMCMLXVI&lt;br /&gt;2967 = MMCMLXVII&lt;br /&gt;2968 = MMCMLXVIII&lt;br /&gt;2969 = MMCMLXIX&lt;br /&gt;2970 = MMCMLXX&lt;br /&gt;2971 = MMCMLXXI&lt;br /&gt;2972 = MMCMLXXII&lt;br /&gt;2973 = MMCMLXXIII&lt;br /&gt;2974 = MMCMLXXIV&lt;br /&gt;2975 = MMCMLXXV&lt;br /&gt;2976 = MMCMLXXVI&lt;br /&gt;2977 = MMCMLXXVII&lt;br /&gt;2978 = MMCMLXXVIII&lt;br /&gt;2979 = MMCMLXXIX&lt;br /&gt;2980 = MMCMLXXX&lt;br /&gt;2981 = MMCMLXXXI&lt;br /&gt;2982 = MMCMLXXXII&lt;br /&gt;2983 = MMCMLXXXIII&lt;br /&gt;2984 = MMCMLXXXIV&lt;br /&gt;2985 = MMCMLXXXV&lt;br /&gt;2986 = MMCMLXXXVI&lt;br /&gt;2987 = MMCMLXXXVII&lt;br /&gt;2988 = MMCMLXXXVIII&lt;br /&gt;2989 = MMCMLXXXIX&lt;br /&gt;2990 = MMCMXC&lt;br /&gt;2991 = MMCMXCI&lt;br /&gt;2992 = MMCMXCII&lt;br /&gt;2993 = MMCMXCIII&lt;br /&gt;2994 = MMCMXCIV&lt;br /&gt;2995 = MMCMXCV&lt;br /&gt;2996 = MMCMXCVI&lt;br /&gt;2997 = MMCMXCVII&lt;br /&gt;2998 = MMCMXCVIII&lt;br /&gt;2999 = MMCMXCIX&lt;br /&gt;3000 = MMM&lt;br /&gt;3001 = MMMI&lt;br /&gt;3002 = MMMII&lt;br /&gt;3003 = MMMIII&lt;br /&gt;3004 = MMMIV&lt;br /&gt;3005 = MMMV&lt;br /&gt;3006 = MMMVI&lt;br /&gt;3007 = MMMVII&lt;br /&gt;3008 = MMMVIII&lt;br /&gt;3009 = MMMIX&lt;br /&gt;3010 = MMMX&lt;br /&gt;3011 = MMMXI&lt;br /&gt;3012 = MMMXII&lt;br /&gt;3013 = MMMXIII&lt;br /&gt;3014 = MMMXIV&lt;br /&gt;3015 = MMMXV&lt;br /&gt;3016 = MMMXVI&lt;br /&gt;3017 = MMMXVII&lt;br /&gt;3018 = MMMXVIII&lt;br /&gt;3019 = MMMXIX&lt;br /&gt;3020 = MMMXX&lt;br /&gt;3021 = MMMXXI&lt;br /&gt;3022 = MMMXXII&lt;br /&gt;3023 = MMMXXIII&lt;br /&gt;3024 = MMMXXIV&lt;br /&gt;3025 = MMMXXV&lt;br /&gt;3026 = MMMXXVI&lt;br /&gt;3027 = MMMXXVII&lt;br /&gt;3028 = MMMXXVIII&lt;br /&gt;3029 = MMMXXIX&lt;br /&gt;3030 = MMMXXX&lt;br /&gt;3031 = MMMXXXI&lt;br /&gt;3032 = MMMXXXII&lt;br /&gt;3033 = MMMXXXIII&lt;br /&gt;3034 = MMMXXXIV&lt;br /&gt;3035 = MMMXXXV&lt;br /&gt;3036 = MMMXXXVI&lt;br /&gt;3037 = MMMXXXVII&lt;br /&gt;3038 = MMMXXXVIII&lt;br /&gt;3039 = MMMXXXIX&lt;br /&gt;3040 = MMMXL&lt;br /&gt;3041 = MMMXLI&lt;br /&gt;3042 = MMMXLII&lt;br /&gt;3043 = MMMXLIII&lt;br /&gt;3044 = MMMXLIV&lt;br /&gt;3045 = MMMXLV&lt;br /&gt;3046 = MMMXLVI&lt;br /&gt;3047 = MMMXLVII&lt;br /&gt;3048 = MMMXLVIII&lt;br /&gt;3049 = MMMXLIX&lt;br /&gt;3050 = MMML&lt;br /&gt;3051 = MMMLI&lt;br /&gt;3052 = MMMLII&lt;br /&gt;3053 = MMMLIII&lt;br /&gt;3054 = MMMLIV&lt;br /&gt;3055 = MMMLV&lt;br /&gt;3056 = MMMLVI&lt;br /&gt;3057 = MMMLVII&lt;br /&gt;3058 = MMMLVIII&lt;br /&gt;3059 = MMMLIX&lt;br /&gt;3060 = MMMLX&lt;br /&gt;3061 = MMMLXI&lt;br /&gt;3062 = MMMLXII&lt;br /&gt;3063 = MMMLXIII&lt;br /&gt;3064 = MMMLXIV&lt;br /&gt;3065 = MMMLXV&lt;br /&gt;3066 = MMMLXVI&lt;br /&gt;3067 = MMMLXVII&lt;br /&gt;3068 = MMMLXVIII&lt;br /&gt;3069 = MMMLXIX&lt;br /&gt;3070 = MMMLXX&lt;br /&gt;3071 = MMMLXXI&lt;br /&gt;3072 = MMMLXXII&lt;br /&gt;3073 = MMMLXXIII&lt;br /&gt;3074 = MMMLXXIV&lt;br /&gt;3075 = MMMLXXV&lt;br /&gt;3076 = MMMLXXVI&lt;br /&gt;3077 = MMMLXXVII&lt;br /&gt;3078 = MMMLXXVIII&lt;br /&gt;3079 = MMMLXXIX&lt;br /&gt;3080 = MMMLXXX&lt;br /&gt;3081 = MMMLXXXI&lt;br /&gt;3082 = MMMLXXXII&lt;br /&gt;3083 = MMMLXXXIII&lt;br /&gt;3084 = MMMLXXXIV&lt;br /&gt;3085 = MMMLXXXV&lt;br /&gt;3086 = MMMLXXXVI&lt;br /&gt;3087 = MMMLXXXVII&lt;br /&gt;3088 = MMMLXXXVIII&lt;br /&gt;3089 = MMMLXXXIX&lt;br /&gt;3090 = MMMXC&lt;br /&gt;3091 = MMMXCI&lt;br /&gt;3092 = MMMXCII&lt;br /&gt;3093 = MMMXCIII&lt;br /&gt;3094 = MMMXCIV&lt;br /&gt;3095 = MMMXCV&lt;br /&gt;3096 = MMMXCVI&lt;br /&gt;3097 = MMMXCVII&lt;br /&gt;3098 = MMMXCVIII&lt;br /&gt;3099 = MMMXCIX&lt;br /&gt;3100 = MMMC&lt;br /&gt;3101 = MMMCI&lt;br /&gt;3102 = MMMCII&lt;br /&gt;3103 = MMMCIII&lt;br /&gt;3104 = MMMCIV&lt;br /&gt;3105 = MMMCV&lt;br /&gt;3106 = MMMCVI&lt;br /&gt;3107 = MMMCVII&lt;br /&gt;3108 = MMMCVIII&lt;br /&gt;3109 = MMMCIX&lt;br /&gt;3110 = MMMCX&lt;br /&gt;3111 = MMMCXI&lt;br /&gt;3112 = MMMCXII&lt;br /&gt;3113 = MMMCXIII&lt;br /&gt;3114 = MMMCXIV&lt;br /&gt;3115 = MMMCXV&lt;br /&gt;3116 = MMMCXVI&lt;br /&gt;3117 = MMMCXVII&lt;br /&gt;3118 = MMMCXVIII&lt;br /&gt;3119 = MMMCXIX&lt;br /&gt;3120 = MMMCXX&lt;br /&gt;3121 = MMMCXXI&lt;br /&gt;3122 = MMMCXXII&lt;br /&gt;3123 = MMMCXXIII&lt;br /&gt;3124 = MMMCXXIV&lt;br /&gt;3125 = MMMCXXV&lt;br /&gt;3126 = MMMCXXVI&lt;br /&gt;3127 = MMMCXXVII&lt;br /&gt;3128 = MMMCXXVIII&lt;br /&gt;3129 = MMMCXXIX&lt;br /&gt;3130 = MMMCXXX&lt;br /&gt;3131 = MMMCXXXI&lt;br /&gt;3132 = MMMCXXXII&lt;br /&gt;3133 = MMMCXXXIII&lt;br /&gt;3134 = MMMCXXXIV&lt;br /&gt;3135 = MMMCXXXV&lt;br /&gt;3136 = MMMCXXXVI&lt;br /&gt;3137 = MMMCXXXVII&lt;br /&gt;3138 = MMMCXXXVIII&lt;br /&gt;3139 = MMMCXXXIX&lt;br /&gt;3140 = MMMCXL&lt;br /&gt;3141 = MMMCXLI&lt;br /&gt;3142 = MMMCXLII&lt;br /&gt;3143 = MMMCXLIII&lt;br /&gt;3144 = MMMCXLIV&lt;br /&gt;3145 = MMMCXLV&lt;br /&gt;3146 = MMMCXLVI&lt;br /&gt;3147 = MMMCXLVII&lt;br /&gt;3148 = MMMCXLVIII&lt;br /&gt;3149 = MMMCXLIX&lt;br /&gt;3150 = MMMCL&lt;br /&gt;3151 = MMMCLI&lt;br /&gt;3152 = MMMCLII&lt;br /&gt;3153 = MMMCLIII&lt;br /&gt;3154 = MMMCLIV&lt;br /&gt;3155 = MMMCLV&lt;br /&gt;3156 = MMMCLVI&lt;br /&gt;3157 = MMMCLVII&lt;br /&gt;3158 = MMMCLVIII&lt;br /&gt;3159 = MMMCLIX&lt;br /&gt;3160 = MMMCLX&lt;br /&gt;3161 = MMMCLXI&lt;br /&gt;3162 = MMMCLXII&lt;br /&gt;3163 = MMMCLXIII&lt;br /&gt;3164 = MMMCLXIV&lt;br /&gt;3165 = MMMCLXV&lt;br /&gt;3166 = MMMCLXVI&lt;br /&gt;3167 = MMMCLXVII&lt;br /&gt;3168 = MMMCLXVIII&lt;br /&gt;3169 = MMMCLXIX&lt;br /&gt;3170 = MMMCLXX&lt;br /&gt;3171 = MMMCLXXI&lt;br /&gt;3172 = MMMCLXXII&lt;br /&gt;3173 = MMMCLXXIII&lt;br /&gt;3174 = MMMCLXXIV&lt;br /&gt;3175 = MMMCLXXV&lt;br /&gt;3176 = MMMCLXXVI&lt;br /&gt;3177 = MMMCLXXVII&lt;br /&gt;3178 = MMMCLXXVIII&lt;br /&gt;3179 = MMMCLXXIX&lt;br /&gt;3180 = MMMCLXXX&lt;br /&gt;3181 = MMMCLXXXI&lt;br /&gt;3182 = MMMCLXXXII&lt;br /&gt;3183 = MMMCLXXXIII&lt;br /&gt;3184 = MMMCLXXXIV&lt;br /&gt;3185 = MMMCLXXXV&lt;br /&gt;3186 = MMMCLXXXVI&lt;br /&gt;3187 = MMMCLXXXVII&lt;br /&gt;3188 = MMMCLXXXVIII&lt;br /&gt;3189 = MMMCLXXXIX&lt;br /&gt;3190 = MMMCXC&lt;br /&gt;3191 = MMMCXCI&lt;br /&gt;3192 = MMMCXCII&lt;br /&gt;3193 = MMMCXCIII&lt;br /&gt;3194 = MMMCXCIV&lt;br /&gt;3195 = MMMCXCV&lt;br /&gt;3196 = MMMCXCVI&lt;br /&gt;3197 = MMMCXCVII&lt;br /&gt;3198 = MMMCXCVIII&lt;br /&gt;3199 = MMMCXCIX&lt;br /&gt;3200 = MMMCC&lt;br /&gt;3201 = MMMCCI&lt;br /&gt;3202 = MMMCCII&lt;br /&gt;3203 = MMMCCIII&lt;br /&gt;3204 = MMMCCIV&lt;br /&gt;3205 = MMMCCV&lt;br /&gt;3206 = MMMCCVI&lt;br /&gt;3207 = MMMCCVII&lt;br /&gt;3208 = MMMCCVIII&lt;br /&gt;3209 = MMMCCIX&lt;br /&gt;3210 = MMMCCX&lt;br /&gt;3211 = MMMCCXI&lt;br /&gt;3212 = MMMCCXII&lt;br /&gt;3213 = MMMCCXIII&lt;br /&gt;3214 = MMMCCXIV&lt;br /&gt;3215 = MMMCCXV&lt;br /&gt;3216 = MMMCCXVI&lt;br /&gt;3217 = MMMCCXVII&lt;br /&gt;3218 = MMMCCXVIII&lt;br /&gt;3219 = MMMCCXIX&lt;br /&gt;3220 = MMMCCXX&lt;br /&gt;3221 = MMMCCXXI&lt;br /&gt;3222 = MMMCCXXII&lt;br /&gt;3223 = MMMCCXXIII&lt;br /&gt;3224 = MMMCCXXIV&lt;br /&gt;3225 = MMMCCXXV&lt;br /&gt;3226 = MMMCCXXVI&lt;br /&gt;3227 = MMMCCXXVII&lt;br /&gt;3228 = MMMCCXXVIII&lt;br /&gt;3229 = MMMCCXXIX&lt;br /&gt;3230 = MMMCCXXX&lt;br /&gt;3231 = MMMCCXXXI&lt;br /&gt;3232 = MMMCCXXXII&lt;br /&gt;3233 = MMMCCXXXIII&lt;br /&gt;3234 = MMMCCXXXIV&lt;br /&gt;3235 = MMMCCXXXV&lt;br /&gt;3236 = MMMCCXXXVI&lt;br /&gt;3237 = MMMCCXXXVII&lt;br /&gt;3238 = MMMCCXXXVIII&lt;br /&gt;3239 = MMMCCXXXIX&lt;br /&gt;3240 = MMMCCXL&lt;br /&gt;3241 = MMMCCXLI&lt;br /&gt;3242 = MMMCCXLII&lt;br /&gt;3243 = MMMCCXLIII&lt;br /&gt;3244 = MMMCCXLIV&lt;br /&gt;3245 = MMMCCXLV&lt;br /&gt;3246 = MMMCCXLVI&lt;br /&gt;3247 = MMMCCXLVII&lt;br /&gt;3248 = MMMCCXLVIII&lt;br /&gt;3249 = MMMCCXLIX&lt;br /&gt;3250 = MMMCCL&lt;br /&gt;3251 = MMMCCLI&lt;br /&gt;3252 = MMMCCLII&lt;br /&gt;3253 = MMMCCLIII&lt;br /&gt;3254 = MMMCCLIV&lt;br /&gt;3255 = MMMCCLV&lt;br /&gt;3256 = MMMCCLVI&lt;br /&gt;3257 = MMMCCLVII&lt;br /&gt;3258 = MMMCCLVIII&lt;br /&gt;3259 = MMMCCLIX&lt;br /&gt;3260 = MMMCCLX&lt;br /&gt;3261 = MMMCCLXI&lt;br /&gt;3262 = MMMCCLXII&lt;br /&gt;3263 = MMMCCLXIII&lt;br /&gt;3264 = MMMCCLXIV&lt;br /&gt;3265 = MMMCCLXV&lt;br /&gt;3266 = MMMCCLXVI&lt;br /&gt;3267 = MMMCCLXVII&lt;br /&gt;3268 = MMMCCLXVIII&lt;br /&gt;3269 = MMMCCLXIX&lt;br /&gt;3270 = MMMCCLXX&lt;br /&gt;3271 = MMMCCLXXI&lt;br /&gt;3272 = MMMCCLXXII&lt;br /&gt;3273 = MMMCCLXXIII&lt;br /&gt;3274 = MMMCCLXXIV&lt;br /&gt;3275 = MMMCCLXXV&lt;br /&gt;3276 = MMMCCLXXVI&lt;br /&gt;3277 = MMMCCLXXVII&lt;br /&gt;3278 = MMMCCLXXVIII&lt;br /&gt;3279 = MMMCCLXXIX&lt;br /&gt;3280 = MMMCCLXXX&lt;br /&gt;3281 = MMMCCLXXXI&lt;br /&gt;3282 = MMMCCLXXXII&lt;br /&gt;3283 = MMMCCLXXXIII&lt;br /&gt;3284 = MMMCCLXXXIV&lt;br /&gt;3285 = MMMCCLXXXV&lt;br /&gt;3286 = MMMCCLXXXVI&lt;br /&gt;3287 = MMMCCLXXXVII&lt;br /&gt;3288 = MMMCCLXXXVIII&lt;br /&gt;3289 = MMMCCLXXXIX&lt;br /&gt;3290 = MMMCCXC&lt;br /&gt;3291 = MMMCCXCI&lt;br /&gt;3292 = MMMCCXCII&lt;br /&gt;3293 = MMMCCXCIII&lt;br /&gt;3294 = MMMCCXCIV&lt;br /&gt;3295 = MMMCCXCV&lt;br /&gt;3296 = MMMCCXCVI&lt;br /&gt;3297 = MMMCCXCVII&lt;br /&gt;3298 = MMMCCXCVIII&lt;br /&gt;3299 = MMMCCXCIX&lt;br /&gt;3300 = MMMCCC&lt;br /&gt;3301 = MMMCCCI&lt;br /&gt;3302 = MMMCCCII&lt;br /&gt;3303 = MMMCCCIII&lt;br /&gt;3304 = MMMCCCIV&lt;br /&gt;3305 = MMMCCCV&lt;br /&gt;3306 = MMMCCCVI&lt;br /&gt;3307 = MMMCCCVII&lt;br /&gt;3308 = MMMCCCVIII&lt;br /&gt;3309 = MMMCCCIX&lt;br /&gt;3310 = MMMCCCX&lt;br /&gt;3311 = MMMCCCXI&lt;br /&gt;3312 = MMMCCCXII&lt;br /&gt;3313 = MMMCCCXIII&lt;br /&gt;3314 = MMMCCCXIV&lt;br /&gt;3315 = MMMCCCXV&lt;br /&gt;3316 = MMMCCCXVI&lt;br /&gt;3317 = MMMCCCXVII&lt;br /&gt;3318 = MMMCCCXVIII&lt;br /&gt;3319 = MMMCCCXIX&lt;br /&gt;3320 = MMMCCCXX&lt;br /&gt;3321 = MMMCCCXXI&lt;br /&gt;3322 = MMMCCCXXII&lt;br /&gt;3323 = MMMCCCXXIII&lt;br /&gt;3324 = MMMCCCXXIV&lt;br /&gt;3325 = MMMCCCXXV&lt;br /&gt;3326 = MMMCCCXXVI&lt;br /&gt;3327 = MMMCCCXXVII&lt;br /&gt;3328 = MMMCCCXXVIII&lt;br /&gt;3329 = MMMCCCXXIX&lt;br /&gt;3330 = MMMCCCXXX&lt;br /&gt;3331 = MMMCCCXXXI&lt;br /&gt;3332 = MMMCCCXXXII&lt;br /&gt;3333 = MMMCCCXXXIII&lt;br /&gt;3334 = MMMCCCXXXIV&lt;br /&gt;3335 = MMMCCCXXXV&lt;br /&gt;3336 = MMMCCCXXXVI&lt;br /&gt;3337 = MMMCCCXXXVII&lt;br /&gt;3338 = MMMCCCXXXVIII&lt;br /&gt;3339 = MMMCCCXXXIX&lt;br /&gt;3340 = MMMCCCXL&lt;br /&gt;3341 = MMMCCCXLI&lt;br /&gt;3342 = MMMCCCXLII&lt;br /&gt;3343 = MMMCCCXLIII&lt;br /&gt;3344 = MMMCCCXLIV&lt;br /&gt;3345 = MMMCCCXLV&lt;br /&gt;3346 = MMMCCCXLVI&lt;br /&gt;3347 = MMMCCCXLVII&lt;br /&gt;3348 = MMMCCCXLVIII&lt;br /&gt;3349 = MMMCCCXLIX&lt;br /&gt;3350 = MMMCCCL&lt;br /&gt;3351 = MMMCCCLI&lt;br /&gt;3352 = MMMCCCLII&lt;br /&gt;3353 = MMMCCCLIII&lt;br /&gt;3354 = MMMCCCLIV&lt;br /&gt;3355 = MMMCCCLV&lt;br /&gt;3356 = MMMCCCLVI&lt;br /&gt;3357 = MMMCCCLVII&lt;br /&gt;3358 = MMMCCCLVIII&lt;br /&gt;3359 = MMMCCCLIX&lt;br /&gt;3360 = MMMCCCLX&lt;br /&gt;3361 = MMMCCCLXI&lt;br /&gt;3362 = MMMCCCLXII&lt;br /&gt;3363 = MMMCCCLXIII&lt;br /&gt;3364 = MMMCCCLXIV&lt;br /&gt;3365 = MMMCCCLXV&lt;br /&gt;3366 = MMMCCCLXVI&lt;br /&gt;3367 = MMMCCCLXVII&lt;br /&gt;3368 = MMMCCCLXVIII&lt;br /&gt;3369 = MMMCCCLXIX&lt;br /&gt;3370 = MMMCCCLXX&lt;br /&gt;3371 = MMMCCCLXXI&lt;br /&gt;3372 = MMMCCCLXXII&lt;br /&gt;3373 = MMMCCCLXXIII&lt;br /&gt;3374 = MMMCCCLXXIV&lt;br /&gt;3375 = MMMCCCLXXV&lt;br /&gt;3376 = MMMCCCLXXVI&lt;br /&gt;3377 = MMMCCCLXXVII&lt;br /&gt;3378 = MMMCCCLXXVIII&lt;br /&gt;3379 = MMMCCCLXXIX&lt;br /&gt;3380 = MMMCCCLXXX&lt;br /&gt;3381 = MMMCCCLXXXI&lt;br /&gt;3382 = MMMCCCLXXXII&lt;br /&gt;3383 = MMMCCCLXXXIII&lt;br /&gt;3384 = MMMCCCLXXXIV&lt;br /&gt;3385 = MMMCCCLXXXV&lt;br /&gt;3386 = MMMCCCLXXXVI&lt;br /&gt;3387 = MMMCCCLXXXVII&lt;br /&gt;3388 = MMMCCCLXXXVIII&lt;br /&gt;3389 = MMMCCCLXXXIX&lt;br /&gt;3390 = MMMCCCXC&lt;br /&gt;3391 = MMMCCCXCI&lt;br /&gt;3392 = MMMCCCXCII&lt;br /&gt;3393 = MMMCCCXCIII&lt;br /&gt;3394 = MMMCCCXCIV&lt;br /&gt;3395 = MMMCCCXCV&lt;br /&gt;3396 = MMMCCCXCVI&lt;br /&gt;3397 = MMMCCCXCVII&lt;br /&gt;3398 = MMMCCCXCVIII&lt;br /&gt;3399 = MMMCCCXCIX&lt;br /&gt;3400 = MMMCD&lt;br /&gt;3401 = MMMCDI&lt;br /&gt;3402 = MMMCDII&lt;br /&gt;3403 = MMMCDIII&lt;br /&gt;3404 = MMMCDIV&lt;br /&gt;3405 = MMMCDV&lt;br /&gt;3406 = MMMCDVI&lt;br /&gt;3407 = MMMCDVII&lt;br /&gt;3408 = MMMCDVIII&lt;br /&gt;3409 = MMMCDIX&lt;br /&gt;3410 = MMMCDX&lt;br /&gt;3411 = MMMCDXI&lt;br /&gt;3412 = MMMCDXII&lt;br /&gt;3413 = MMMCDXIII&lt;br /&gt;3414 = MMMCDXIV&lt;br /&gt;3415 = MMMCDXV&lt;br /&gt;3416 = MMMCDXVI&lt;br /&gt;3417 = MMMCDXVII&lt;br /&gt;3418 = MMMCDXVIII&lt;br /&gt;3419 = MMMCDXIX&lt;br /&gt;3420 = MMMCDXX&lt;br /&gt;3421 = MMMCDXXI&lt;br /&gt;3422 = MMMCDXXII&lt;br /&gt;3423 = MMMCDXXIII&lt;br /&gt;3424 = MMMCDXXIV&lt;br /&gt;3425 = MMMCDXXV&lt;br /&gt;3426 = MMMCDXXVI&lt;br /&gt;3427 = MMMCDXXVII&lt;br /&gt;3428 = MMMCDXXVIII&lt;br /&gt;3429 = MMMCDXXIX&lt;br /&gt;3430 = MMMCDXXX&lt;br /&gt;3431 = MMMCDXXXI&lt;br /&gt;3432 = MMMCDXXXII&lt;br /&gt;3433 = MMMCDXXXIII&lt;br /&gt;3434 = MMMCDXXXIV&lt;br /&gt;3435 = MMMCDXXXV&lt;br /&gt;3436 = MMMCDXXXVI&lt;br /&gt;3437 = MMMCDXXXVII&lt;br /&gt;3438 = MMMCDXXXVIII&lt;br /&gt;3439 = MMMCDXXXIX&lt;br /&gt;3440 = MMMCDXL&lt;br /&gt;3441 = MMMCDXLI&lt;br /&gt;3442 = MMMCDXLII&lt;br /&gt;3443 = MMMCDXLIII&lt;br /&gt;3444 = MMMCDXLIV&lt;br /&gt;3445 = MMMCDXLV&lt;br /&gt;3446 = MMMCDXLVI&lt;br /&gt;3447 = MMMCDXLVII&lt;br /&gt;3448 = MMMCDXLVIII&lt;br /&gt;3449 = MMMCDXLIX&lt;br /&gt;3450 = MMMCDL&lt;br /&gt;3451 = MMMCDLI&lt;br /&gt;3452 = MMMCDLII&lt;br /&gt;3453 = MMMCDLIII&lt;br /&gt;3454 = MMMCDLIV&lt;br /&gt;3455 = MMMCDLV&lt;br /&gt;3456 = MMMCDLVI&lt;br /&gt;3457 = MMMCDLVII&lt;br /&gt;3458 = MMMCDLVIII&lt;br /&gt;3459 = MMMCDLIX&lt;br /&gt;3460 = MMMCDLX&lt;br /&gt;3461 = MMMCDLXI&lt;br /&gt;3462 = MMMCDLXII&lt;br /&gt;3463 = MMMCDLXIII&lt;br /&gt;3464 = MMMCDLXIV&lt;br /&gt;3465 = MMMCDLXV&lt;br /&gt;3466 = MMMCDLXVI&lt;br /&gt;3467 = MMMCDLXVII&lt;br /&gt;3468 = MMMCDLXVIII&lt;br /&gt;3469 = MMMCDLXIX&lt;br /&gt;3470 = MMMCDLXX&lt;br /&gt;3471 = MMMCDLXXI&lt;br /&gt;3472 = MMMCDLXXII&lt;br /&gt;3473 = MMMCDLXXIII&lt;br /&gt;3474 = MMMCDLXXIV&lt;br /&gt;3475 = MMMCDLXXV&lt;br /&gt;3476 = MMMCDLXXVI&lt;br /&gt;3477 = MMMCDLXXVII&lt;br /&gt;3478 = MMMCDLXXVIII&lt;br /&gt;3479 = MMMCDLXXIX&lt;br /&gt;3480 = MMMCDLXXX&lt;br /&gt;3481 = MMMCDLXXXI&lt;br /&gt;3482 = MMMCDLXXXII&lt;br /&gt;3483 = MMMCDLXXXIII&lt;br /&gt;3484 = MMMCDLXXXIV&lt;br /&gt;3485 = MMMCDLXXXV&lt;br /&gt;3486 = MMMCDLXXXVI&lt;br /&gt;3487 = MMMCDLXXXVII&lt;br /&gt;3488 = MMMCDLXXXVIII&lt;br /&gt;3489 = MMMCDLXXXIX&lt;br /&gt;3490 = MMMCDXC&lt;br /&gt;3491 = MMMCDXCI&lt;br /&gt;3492 = MMMCDXCII&lt;br /&gt;3493 = MMMCDXCIII&lt;br /&gt;3494 = MMMCDXCIV&lt;br /&gt;3495 = MMMCDXCV&lt;br /&gt;3496 = MMMCDXCVI&lt;br /&gt;3497 = MMMCDXCVII&lt;br /&gt;3498 = MMMCDXCVIII&lt;br /&gt;3499 = MMMCDXCIX&lt;br /&gt;3500 = MMMD&lt;br /&gt;3501 = MMMDI&lt;br /&gt;3502 = MMMDII&lt;br /&gt;3503 = MMMDIII&lt;br /&gt;3504 = MMMDIV&lt;br /&gt;3505 = MMMDV&lt;br /&gt;3506 = MMMDVI&lt;br /&gt;3507 = MMMDVII&lt;br /&gt;3508 = MMMDVIII&lt;br /&gt;3509 = MMMDIX&lt;br /&gt;3510 = MMMDX&lt;br /&gt;3511 = MMMDXI&lt;br /&gt;3512 = MMMDXII&lt;br /&gt;3513 = MMMDXIII&lt;br /&gt;3514 = MMMDXIV&lt;br /&gt;3515 = MMMDXV&lt;br /&gt;3516 = MMMDXVI&lt;br /&gt;3517 = MMMDXVII&lt;br /&gt;3518 = MMMDXVIII&lt;br /&gt;3519 = MMMDXIX&lt;br /&gt;3520 = MMMDXX&lt;br /&gt;3521 = MMMDXXI&lt;br /&gt;3522 = MMMDXXII&lt;br /&gt;3523 = MMMDXXIII&lt;br /&gt;3524 = MMMDXXIV&lt;br /&gt;3525 = MMMDXXV&lt;br /&gt;3526 = MMMDXXVI&lt;br /&gt;3527 = MMMDXXVII&lt;br /&gt;3528 = MMMDXXVIII&lt;br /&gt;3529 = MMMDXXIX&lt;br /&gt;3530 = MMMDXXX&lt;br /&gt;3531 = MMMDXXXI&lt;br /&gt;3532 = MMMDXXXII&lt;br /&gt;3533 = MMMDXXXIII&lt;br /&gt;3534 = MMMDXXXIV&lt;br /&gt;3535 = MMMDXXXV&lt;br /&gt;3536 = MMMDXXXVI&lt;br /&gt;3537 = MMMDXXXVII&lt;br /&gt;3538 = MMMDXXXVIII&lt;br /&gt;3539 = MMMDXXXIX&lt;br /&gt;3540 = MMMDXL&lt;br /&gt;3541 = MMMDXLI&lt;br /&gt;3542 = MMMDXLII&lt;br /&gt;3543 = MMMDXLIII&lt;br /&gt;3544 = MMMDXLIV&lt;br /&gt;3545 = MMMDXLV&lt;br /&gt;3546 = MMMDXLVI&lt;br /&gt;3547 = MMMDXLVII&lt;br /&gt;3548 = MMMDXLVIII&lt;br /&gt;3549 = MMMDXLIX&lt;br /&gt;3550 = MMMDL&lt;br /&gt;3551 = MMMDLI&lt;br /&gt;3552 = MMMDLII&lt;br /&gt;3553 = MMMDLIII&lt;br /&gt;3554 = MMMDLIV&lt;br /&gt;3555 = MMMDLV&lt;br /&gt;3556 = MMMDLVI&lt;br /&gt;3557 = MMMDLVII&lt;br /&gt;3558 = MMMDLVIII&lt;br /&gt;3559 = MMMDLIX&lt;br /&gt;3560 = MMMDLX&lt;br /&gt;3561 = MMMDLXI&lt;br /&gt;3562 = MMMDLXII&lt;br /&gt;3563 = MMMDLXIII&lt;br /&gt;3564 = MMMDLXIV&lt;br /&gt;3565 = MMMDLXV&lt;br /&gt;3566 = MMMDLXVI&lt;br /&gt;3567 = MMMDLXVII&lt;br /&gt;3568 = MMMDLXVIII&lt;br /&gt;3569 = MMMDLXIX&lt;br /&gt;3570 = MMMDLXX&lt;br /&gt;3571 = MMMDLXXI&lt;br /&gt;3572 = MMMDLXXII&lt;br /&gt;3573 = MMMDLXXIII&lt;br /&gt;3574 = MMMDLXXIV&lt;br /&gt;3575 = MMMDLXXV&lt;br /&gt;3576 = MMMDLXXVI&lt;br /&gt;3577 = MMMDLXXVII&lt;br /&gt;3578 = MMMDLXXVIII&lt;br /&gt;3579 = MMMDLXXIX&lt;br /&gt;3580 = MMMDLXXX&lt;br /&gt;3581 = MMMDLXXXI&lt;br /&gt;3582 = MMMDLXXXII&lt;br /&gt;3583 = MMMDLXXXIII&lt;br /&gt;3584 = MMMDLXXXIV&lt;br /&gt;3585 = MMMDLXXXV&lt;br /&gt;3586 = MMMDLXXXVI&lt;br /&gt;3587 = MMMDLXXXVII&lt;br /&gt;3588 = MMMDLXXXVIII&lt;br /&gt;3589 = MMMDLXXXIX&lt;br /&gt;3590 = MMMDXC&lt;br /&gt;3591 = MMMDXCI&lt;br /&gt;3592 = MMMDXCII&lt;br /&gt;3593 = MMMDXCIII&lt;br /&gt;3594 = MMMDXCIV&lt;br /&gt;3595 = MMMDXCV&lt;br /&gt;3596 = MMMDXCVI&lt;br /&gt;3597 = MMMDXCVII&lt;br /&gt;3598 = MMMDXCVIII&lt;br /&gt;3599 = MMMDXCIX&lt;br /&gt;3600 = MMMDC&lt;br /&gt;3601 = MMMDCI&lt;br /&gt;3602 = MMMDCII&lt;br /&gt;3603 = MMMDCIII&lt;br /&gt;3604 = MMMDCIV&lt;br /&gt;3605 = MMMDCV&lt;br /&gt;3606 = MMMDCVI&lt;br /&gt;3607 = MMMDCVII&lt;br /&gt;3608 = MMMDCVIII&lt;br /&gt;3609 = MMMDCIX&lt;br /&gt;3610 = MMMDCX&lt;br /&gt;3611 = MMMDCXI&lt;br /&gt;3612 = MMMDCXII&lt;br /&gt;3613 = MMMDCXIII&lt;br /&gt;3614 = MMMDCXIV&lt;br /&gt;3615 = MMMDCXV&lt;br /&gt;3616 = MMMDCXVI&lt;br /&gt;3617 = MMMDCXVII&lt;br /&gt;3618 = MMMDCXVIII&lt;br /&gt;3619 = MMMDCXIX&lt;br /&gt;3620 = MMMDCXX&lt;br /&gt;3621 = MMMDCXXI&lt;br /&gt;3622 = MMMDCXXII&lt;br /&gt;3623 = MMMDCXXIII&lt;br /&gt;3624 = MMMDCXXIV&lt;br /&gt;3625 = MMMDCXXV&lt;br /&gt;3626 = MMMDCXXVI&lt;br /&gt;3627 = MMMDCXXVII&lt;br /&gt;3628 = MMMDCXXVIII&lt;br /&gt;3629 = MMMDCXXIX&lt;br /&gt;3630 = MMMDCXXX&lt;br /&gt;3631 = MMMDCXXXI&lt;br /&gt;3632 = MMMDCXXXII&lt;br /&gt;3633 = MMMDCXXXIII&lt;br /&gt;3634 = MMMDCXXXIV&lt;br /&gt;3635 = MMMDCXXXV&lt;br /&gt;3636 = MMMDCXXXVI&lt;br /&gt;3637 = MMMDCXXXVII&lt;br /&gt;3638 = MMMDCXXXVIII&lt;br /&gt;3639 = MMMDCXXXIX&lt;br /&gt;3640 = MMMDCXL&lt;br /&gt;3641 = MMMDCXLI&lt;br /&gt;3642 = MMMDCXLII&lt;br /&gt;3643 = MMMDCXLIII&lt;br /&gt;3644 = MMMDCXLIV&lt;br /&gt;3645 = MMMDCXLV&lt;br /&gt;3646 = MMMDCXLVI&lt;br /&gt;3647 = MMMDCXLVII&lt;br /&gt;3648 = MMMDCXLVIII&lt;br /&gt;3649 = MMMDCXLIX&lt;br /&gt;3650 = MMMDCL&lt;br /&gt;3651 = MMMDCLI&lt;br /&gt;3652 = MMMDCLII&lt;br /&gt;3653 = MMMDCLIII&lt;br /&gt;3654 = MMMDCLIV&lt;br /&gt;3655 = MMMDCLV&lt;br /&gt;3656 = MMMDCLVI&lt;br /&gt;3657 = MMMDCLVII&lt;br /&gt;3658 = MMMDCLVIII&lt;br /&gt;3659 = MMMDCLIX&lt;br /&gt;3660 = MMMDCLX&lt;br /&gt;3661 = MMMDCLXI&lt;br /&gt;3662 = MMMDCLXII&lt;br /&gt;3663 = MMMDCLXIII&lt;br /&gt;3664 = MMMDCLXIV&lt;br /&gt;3665 = MMMDCLXV&lt;br /&gt;3666 = MMMDCLXVI&lt;br /&gt;3667 = MMMDCLXVII&lt;br /&gt;3668 = MMMDCLXVIII&lt;br /&gt;3669 = MMMDCLXIX&lt;br /&gt;3670 = MMMDCLXX&lt;br /&gt;3671 = MMMDCLXXI&lt;br /&gt;3672 = MMMDCLXXII&lt;br /&gt;3673 = MMMDCLXXIII&lt;br /&gt;3674 = MMMDCLXXIV&lt;br /&gt;3675 = MMMDCLXXV&lt;br /&gt;3676 = MMMDCLXXVI&lt;br /&gt;3677 = MMMDCLXXVII&lt;br /&gt;3678 = MMMDCLXXVIII&lt;br /&gt;3679 = MMMDCLXXIX&lt;br /&gt;3680 = MMMDCLXXX&lt;br /&gt;3681 = MMMDCLXXXI&lt;br /&gt;3682 = MMMDCLXXXII&lt;br /&gt;3683 = MMMDCLXXXIII&lt;br /&gt;3684 = MMMDCLXXXIV&lt;br /&gt;3685 = MMMDCLXXXV&lt;br /&gt;3686 = MMMDCLXXXVI&lt;br /&gt;3687 = MMMDCLXXXVII&lt;br /&gt;3688 = MMMDCLXXXVIII&lt;br /&gt;3689 = MMMDCLXXXIX&lt;br /&gt;3690 = MMMDCXC&lt;br /&gt;3691 = MMMDCXCI&lt;br /&gt;3692 = MMMDCXCII&lt;br /&gt;3693 = MMMDCXCIII&lt;br /&gt;3694 = MMMDCXCIV&lt;br /&gt;3695 = MMMDCXCV&lt;br /&gt;3696 = MMMDCXCVI&lt;br /&gt;3697 = MMMDCXCVII&lt;br /&gt;3698 = MMMDCXCVIII&lt;br /&gt;3699 = MMMDCXCIX&lt;br /&gt;3700 = MMMDCC&lt;br /&gt;3701 = MMMDCCI&lt;br /&gt;3702 = MMMDCCII&lt;br /&gt;3703 = MMMDCCIII&lt;br /&gt;3704 = MMMDCCIV&lt;br /&gt;3705 = MMMDCCV&lt;br /&gt;3706 = MMMDCCVI&lt;br /&gt;3707 = MMMDCCVII&lt;br /&gt;3708 = MMMDCCVIII&lt;br /&gt;3709 = MMMDCCIX&lt;br /&gt;3710 = MMMDCCX&lt;br /&gt;3711 = MMMDCCXI&lt;br /&gt;3712 = MMMDCCXII&lt;br /&gt;3713 = MMMDCCXIII&lt;br /&gt;3714 = MMMDCCXIV&lt;br /&gt;3715 = MMMDCCXV&lt;br /&gt;3716 = MMMDCCXVI&lt;br /&gt;3717 = MMMDCCXVII&lt;br /&gt;3718 = MMMDCCXVIII&lt;br /&gt;3719 = MMMDCCXIX&lt;br /&gt;3720 = MMMDCCXX&lt;br /&gt;3721 = MMMDCCXXI&lt;br /&gt;3722 = MMMDCCXXII&lt;br /&gt;3723 = MMMDCCXXIII&lt;br /&gt;3724 = MMMDCCXXIV&lt;br /&gt;3725 = MMMDCCXXV&lt;br /&gt;3726 = MMMDCCXXVI&lt;br /&gt;3727 = MMMDCCXXVII&lt;br /&gt;3728 = MMMDCCXXVIII&lt;br /&gt;3729 = MMMDCCXXIX&lt;br /&gt;3730 = MMMDCCXXX&lt;br /&gt;3731 = MMMDCCXXXI&lt;br /&gt;3732 = MMMDCCXXXII&lt;br /&gt;3733 = MMMDCCXXXIII&lt;br /&gt;3734 = MMMDCCXXXIV&lt;br /&gt;3735 = MMMDCCXXXV&lt;br /&gt;3736 = MMMDCCXXXVI&lt;br /&gt;3737 = MMMDCCXXXVII&lt;br /&gt;3738 = MMMDCCXXXVIII&lt;br /&gt;3739 = MMMDCCXXXIX&lt;br /&gt;3740 = MMMDCCXL&lt;br /&gt;3741 = MMMDCCXLI&lt;br /&gt;3742 = MMMDCCXLII&lt;br /&gt;3743 = MMMDCCXLIII&lt;br /&gt;3744 = MMMDCCXLIV&lt;br /&gt;3745 = MMMDCCXLV&lt;br /&gt;3746 = MMMDCCXLVI&lt;br /&gt;3747 = MMMDCCXLVII&lt;br /&gt;3748 = MMMDCCXLVIII&lt;br /&gt;3749 = MMMDCCXLIX&lt;br /&gt;3750 = MMMDCCL&lt;br /&gt;3751 = MMMDCCLI&lt;br /&gt;3752 = MMMDCCLII&lt;br /&gt;3753 = MMMDCCLIII&lt;br /&gt;3754 = MMMDCCLIV&lt;br /&gt;3755 = MMMDCCLV&lt;br /&gt;3756 = MMMDCCLVI&lt;br /&gt;3757 = MMMDCCLVII&lt;br /&gt;3758 = MMMDCCLVIII&lt;br /&gt;3759 = MMMDCCLIX&lt;br /&gt;3760 = MMMDCCLX&lt;br /&gt;3761 = MMMDCCLXI&lt;br /&gt;3762 = MMMDCCLXII&lt;br /&gt;3763 = MMMDCCLXIII&lt;br /&gt;3764 = MMMDCCLXIV&lt;br /&gt;3765 = MMMDCCLXV&lt;br /&gt;3766 = MMMDCCLXVI&lt;br /&gt;3767 = MMMDCCLXVII&lt;br /&gt;3768 = MMMDCCLXVIII&lt;br /&gt;3769 = MMMDCCLXIX&lt;br /&gt;3770 = MMMDCCLXX&lt;br /&gt;3771 = MMMDCCLXXI&lt;br /&gt;3772 = MMMDCCLXXII&lt;br /&gt;3773 = MMMDCCLXXIII&lt;br /&gt;3774 = MMMDCCLXXIV&lt;br /&gt;3775 = MMMDCCLXXV&lt;br /&gt;3776 = MMMDCCLXXVI&lt;br /&gt;3777 = MMMDCCLXXVII&lt;br /&gt;3778 = MMMDCCLXXVIII&lt;br /&gt;3779 = MMMDCCLXXIX&lt;br /&gt;3780 = MMMDCCLXXX&lt;br /&gt;3781 = MMMDCCLXXXI&lt;br /&gt;3782 = MMMDCCLXXXII&lt;br /&gt;3783 = MMMDCCLXXXIII&lt;br /&gt;3784 = MMMDCCLXXXIV&lt;br /&gt;3785 = MMMDCCLXXXV&lt;br /&gt;3786 = MMMDCCLXXXVI&lt;br /&gt;3787 = MMMDCCLXXXVII&lt;br /&gt;3788 = MMMDCCLXXXVIII&lt;br /&gt;3789 = MMMDCCLXXXIX&lt;br /&gt;3790 = MMMDCCXC&lt;br /&gt;3791 = MMMDCCXCI&lt;br /&gt;3792 = MMMDCCXCII&lt;br /&gt;3793 = MMMDCCXCIII&lt;br /&gt;3794 = MMMDCCXCIV&lt;br /&gt;3795 = MMMDCCXCV&lt;br /&gt;3796 = MMMDCCXCVI&lt;br /&gt;3797 = MMMDCCXCVII&lt;br /&gt;3798 = MMMDCCXCVIII&lt;br /&gt;3799 = MMMDCCXCIX&lt;br /&gt;3800 = MMMDCCC&lt;br /&gt;3801 = MMMDCCCI&lt;br /&gt;3802 = MMMDCCCII&lt;br /&gt;3803 = MMMDCCCIII&lt;br /&gt;3804 = MMMDCCCIV&lt;br /&gt;3805 = MMMDCCCV&lt;br /&gt;3806 = MMMDCCCVI&lt;br /&gt;3807 = MMMDCCCVII&lt;br /&gt;3808 = MMMDCCCVIII&lt;br /&gt;3809 = MMMDCCCIX&lt;br /&gt;3810 = MMMDCCCX&lt;br /&gt;3811 = MMMDCCCXI&lt;br /&gt;3812 = MMMDCCCXII&lt;br /&gt;3813 = MMMDCCCXIII&lt;br /&gt;3814 = MMMDCCCXIV&lt;br /&gt;3815 = MMMDCCCXV&lt;br /&gt;3816 = MMMDCCCXVI&lt;br /&gt;3817 = MMMDCCCXVII&lt;br /&gt;3818 = MMMDCCCXVIII&lt;br /&gt;3819 = MMMDCCCXIX&lt;br /&gt;3820 = MMMDCCCXX&lt;br /&gt;3821 = MMMDCCCXXI&lt;br /&gt;3822 = MMMDCCCXXII&lt;br /&gt;3823 = MMMDCCCXXIII&lt;br /&gt;3824 = MMMDCCCXXIV&lt;br /&gt;3825 = MMMDCCCXXV&lt;br /&gt;3826 = MMMDCCCXXVI&lt;br /&gt;3827 = MMMDCCCXXVII&lt;br /&gt;3828 = MMMDCCCXXVIII&lt;br /&gt;3829 = MMMDCCCXXIX&lt;br /&gt;3830 = MMMDCCCXXX&lt;br /&gt;3831 = MMMDCCCXXXI&lt;br /&gt;3832 = MMMDCCCXXXII&lt;br /&gt;3833 = MMMDCCCXXXIII&lt;br /&gt;3834 = MMMDCCCXXXIV&lt;br /&gt;3835 = MMMDCCCXXXV&lt;br /&gt;3836 = MMMDCCCXXXVI&lt;br /&gt;3837 = MMMDCCCXXXVII&lt;br /&gt;3838 = MMMDCCCXXXVIII&lt;br /&gt;3839 = MMMDCCCXXXIX&lt;br /&gt;3840 = MMMDCCCXL&lt;br /&gt;3841 = MMMDCCCXLI&lt;br /&gt;3842 = MMMDCCCXLII&lt;br /&gt;3843 = MMMDCCCXLIII&lt;br /&gt;3844 = MMMDCCCXLIV&lt;br /&gt;3845 = MMMDCCCXLV&lt;br /&gt;3846 = MMMDCCCXLVI&lt;br /&gt;3847 = MMMDCCCXLVII&lt;br /&gt;3848 = MMMDCCCXLVIII&lt;br /&gt;3849 = MMMDCCCXLIX&lt;br /&gt;3850 = MMMDCCCL&lt;br /&gt;3851 = MMMDCCCLI&lt;br /&gt;3852 = MMMDCCCLII&lt;br /&gt;3853 = MMMDCCCLIII&lt;br /&gt;3854 = MMMDCCCLIV&lt;br /&gt;3855 = MMMDCCCLV&lt;br /&gt;3856 = MMMDCCCLVI&lt;br /&gt;3857 = MMMDCCCLVII&lt;br /&gt;3858 = MMMDCCCLVIII&lt;br /&gt;3859 = MMMDCCCLIX&lt;br /&gt;3860 = MMMDCCCLX&lt;br /&gt;3861 = MMMDCCCLXI&lt;br /&gt;3862 = MMMDCCCLXII&lt;br /&gt;3863 = MMMDCCCLXIII&lt;br /&gt;3864 = MMMDCCCLXIV&lt;br /&gt;3865 = MMMDCCCLXV&lt;br /&gt;3866 = MMMDCCCLXVI&lt;br /&gt;3867 = MMMDCCCLXVII&lt;br /&gt;3868 = MMMDCCCLXVIII&lt;br /&gt;3869 = MMMDCCCLXIX&lt;br /&gt;3870 = MMMDCCCLXX&lt;br /&gt;3871 = MMMDCCCLXXI&lt;br /&gt;3872 = MMMDCCCLXXII&lt;br /&gt;3873 = MMMDCCCLXXIII&lt;br /&gt;3874 = MMMDCCCLXXIV&lt;br /&gt;3875 = MMMDCCCLXXV&lt;br /&gt;3876 = MMMDCCCLXXVI&lt;br /&gt;3877 = MMMDCCCLXXVII&lt;br /&gt;3878 = MMMDCCCLXXVIII&lt;br /&gt;3879 = MMMDCCCLXXIX&lt;br /&gt;3880 = MMMDCCCLXXX&lt;br /&gt;3881 = MMMDCCCLXXXI&lt;br /&gt;3882 = MMMDCCCLXXXII&lt;br /&gt;3883 = MMMDCCCLXXXIII&lt;br /&gt;3884 = MMMDCCCLXXXIV&lt;br /&gt;3885 = MMMDCCCLXXXV&lt;br /&gt;3886 = MMMDCCCLXXXVI&lt;br /&gt;3887 = MMMDCCCLXXXVII&lt;br /&gt;3888 = MMMDCCCLXXXVIII&lt;br /&gt;3889 = MMMDCCCLXXXIX&lt;br /&gt;3890 = MMMDCCCXC&lt;br /&gt;3891 = MMMDCCCXCI&lt;br /&gt;3892 = MMMDCCCXCII&lt;br /&gt;3893 = MMMDCCCXCIII&lt;br /&gt;3894 = MMMDCCCXCIV&lt;br /&gt;3895 = MMMDCCCXCV&lt;br /&gt;3896 = MMMDCCCXCVI&lt;br /&gt;3897 = MMMDCCCXCVII&lt;br /&gt;3898 = MMMDCCCXCVIII&lt;br /&gt;3899 = MMMDCCCXCIX&lt;br /&gt;3900 = MMMCM&lt;br /&gt;3901 = MMMCMI&lt;br /&gt;3902 = MMMCMII&lt;br /&gt;3903 = MMMCMIII&lt;br /&gt;3904 = MMMCMIV&lt;br /&gt;3905 = MMMCMV&lt;br /&gt;3906 = MMMCMVI&lt;br /&gt;3907 = MMMCMVII&lt;br /&gt;3908 = MMMCMVIII&lt;br /&gt;3909 = MMMCMIX&lt;br /&gt;3910 = MMMCMX&lt;br /&gt;3911 = MMMCMXI&lt;br /&gt;3912 = MMMCMXII&lt;br /&gt;3913 = MMMCMXIII&lt;br /&gt;3914 = MMMCMXIV&lt;br /&gt;3915 = MMMCMXV&lt;br /&gt;3916 = MMMCMXVI&lt;br /&gt;3917 = MMMCMXVII&lt;br /&gt;3918 = MMMCMXVIII&lt;br /&gt;3919 = MMMCMXIX&lt;br /&gt;3920 = MMMCMXX&lt;br /&gt;3921 = MMMCMXXI&lt;br /&gt;3922 = MMMCMXXII&lt;br /&gt;3923 = MMMCMXXIII&lt;br /&gt;3924 = MMMCMXXIV&lt;br /&gt;3925 = MMMCMXXV&lt;br /&gt;3926 = MMMCMXXVI&lt;br /&gt;3927 = MMMCMXXVII&lt;br /&gt;3928 = MMMCMXXVIII&lt;br /&gt;3929 = MMMCMXXIX&lt;br /&gt;3930 = MMMCMXXX&lt;br /&gt;3931 = MMMCMXXXI&lt;br /&gt;3932 = MMMCMXXXII&lt;br /&gt;3933 = MMMCMXXXIII&lt;br /&gt;3934 = MMMCMXXXIV&lt;br /&gt;3935 = MMMCMXXXV&lt;br /&gt;3936 = MMMCMXXXVI&lt;br /&gt;3937 = MMMCMXXXVII&lt;br /&gt;3938 = MMMCMXXXVIII&lt;br /&gt;3939 = MMMCMXXXIX&lt;br /&gt;3940 = MMMCMXL&lt;br /&gt;3941 = MMMCMXLI&lt;br /&gt;3942 = MMMCMXLII&lt;br /&gt;3943 = MMMCMXLIII&lt;br /&gt;3944 = MMMCMXLIV&lt;br /&gt;3945 = MMMCMXLV&lt;br /&gt;3946 = MMMCMXLVI&lt;br /&gt;3947 = MMMCMXLVII&lt;br /&gt;3948 = MMMCMXLVIII&lt;br /&gt;3949 = MMMCMXLIX&lt;br /&gt;3950 = MMMCML&lt;br /&gt;3951 = MMMCMLI&lt;br /&gt;3952 = MMMCMLII&lt;br /&gt;3953 = MMMCMLIII&lt;br /&gt;3954 = MMMCMLIV&lt;br /&gt;3955 = MMMCMLV&lt;br /&gt;3956 = MMMCMLVI&lt;br /&gt;3957 = MMMCMLVII&lt;br /&gt;3958 = MMMCMLVIII&lt;br /&gt;3959 = MMMCMLIX&lt;br /&gt;3960 = MMMCMLX&lt;br /&gt;3961 = MMMCMLXI&lt;br /&gt;3962 = MMMCMLXII&lt;br /&gt;3963 = MMMCMLXIII&lt;br /&gt;3964 = MMMCMLXIV&lt;br /&gt;3965 = MMMCMLXV&lt;br /&gt;3966 = MMMCMLXVI&lt;br /&gt;3967 = MMMCMLXVII&lt;br /&gt;3968 = MMMCMLXVIII&lt;br /&gt;3969 = MMMCMLXIX&lt;br /&gt;3970 = MMMCMLXX&lt;br /&gt;3971 = MMMCMLXXI&lt;br /&gt;3972 = MMMCMLXXII&lt;br /&gt;3973 = MMMCMLXXIII&lt;br /&gt;3974 = MMMCMLXXIV&lt;br /&gt;3975 = MMMCMLXXV&lt;br /&gt;3976 = MMMCMLXXVI&lt;br /&gt;3977 = MMMCMLXXVII&lt;br /&gt;3978 = MMMCMLXXVIII&lt;br /&gt;3979 = MMMCMLXXIX&lt;br /&gt;3980 = MMMCMLXXX&lt;br /&gt;3981 = MMMCMLXXXI&lt;br /&gt;3982 = MMMCMLXXXII&lt;br /&gt;3983 = MMMCMLXXXIII&lt;br /&gt;3984 = MMMCMLXXXIV&lt;br /&gt;3985 = MMMCMLXXXV&lt;br /&gt;3986 = MMMCMLXXXVI&lt;br /&gt;3987 = MMMCMLXXXVII&lt;br /&gt;3988 = MMMCMLXXXVIII&lt;br /&gt;3989 = MMMCMLXXXIX&lt;br /&gt;3990 = MMMCMXC&lt;br /&gt;3991 = MMMCMXCI&lt;br /&gt;3992 = MMMCMXCII&lt;br /&gt;3993 = MMMCMXCIII&lt;br /&gt;3994 = MMMCMXCIV&lt;br /&gt;3995 = MMMCMXCV&lt;br /&gt;3996 = MMMCMXCVI&lt;br /&gt;3997 = MMMCMXCVII&lt;br /&gt;3998 = MMMCMXCVIII&lt;br /&gt;3999 = MMMCMXCIX&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11885757-757497807665613101?l=cautery.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://cautery.blogspot.com/feeds/757497807665613101/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://cautery.blogspot.com/2010/10/javascript-roman-numeral-calculator.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11885757/posts/default/757497807665613101'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11885757/posts/default/757497807665613101'/><link rel='alternate' type='text/html' href='http://cautery.blogspot.com/2010/10/javascript-roman-numeral-calculator.html' title='JavaScript Roman numeral calculator'/><author><name>Curtis Autery</name><uri>https://profiles.google.com/107677530285177731535</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh4.googleusercontent.com/--J3C_khtLqQ/AAAAAAAAAAI/AAAAAAAADT4/e2Qa5ncfAMk/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11885757.post-2096171650018831301</id><published>2010-10-07T20:30:00.002-04:00</published><updated>2010-10-10T05:49:49.717-04:00</updated><title type='text'>Korn shell hack of the day</title><content type='html'>or, "Scope creep and you, a typical example of the modern IT project"&lt;br /&gt;&lt;br /&gt;Sometimes I find myself knee deep in a complicated shell script that probably would have been better off written in perl or Java. Today was one of those days. What started as a simple script I wrote for managing sftp traffic to a couple vendors has been transmogrified to handle calling Glub to do an ftps transfer (the "other" secure FTP) from a vendor, then delivering the files to an internal Windows share drive. So far so good, but feature bloat ended up straining my creativity to tackle each new problem, and ultimately saddled me with a workable solution that, while interesting, is sort of a stinker.&lt;br /&gt;&lt;br /&gt;The connection to the Windows share was supposed to be via a mounted drive. I would copy the files to a Unix directory, and Unix would manage the SMB transfer transparently. This turned out to be unsupported by our data services group unless the Windows server in question was virtual. It was not. So suddenly I'm on the hook for adding SMB support to the process.&lt;br /&gt;&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;A quick search revealed the JCIFS library, which I was able to turn into a Java class that takes commandline inputs for destination server and path, and reads credentials from a config file. Just call "java ToShare" instead of Unix's "cp", and Bob's your uncle. Of course, the script has to grow a little bit to handle the extra config variables, where to find java, and what the classpath should be.&lt;br /&gt;&lt;br /&gt;Second, the Windows guy was having problems automating unzipping the files before feeding them to his app. For whatever reason, the approach we settled on was to have my script unzip the files, and send him the artifacts AND the original .zip files. So the script needed to grow in complexity, managing unzipping and iterating through the unzipped artifacts, and the source archives, and sending each over my java SMB hack. Which looks like this:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;# Simple log function. Echo the date and passed message to the logfile&lt;br /&gt;logit(){&lt;br /&gt;  echo "`date` $1" &amp;gt;&amp;gt; $LOGFILE&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;# If there's an error, call this, which sends a notification email and exits&lt;br /&gt;error_handler() {&lt;br /&gt;  echo "$1" | mailx -s"FTPS receive script failure" $ERROR_EMAIL_ADDRESS&lt;br /&gt;  logit "$1"&lt;br /&gt;  exit 1&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;# Error checker which run after most commands, if the return code from the previous command&lt;br /&gt;# isn't 0, give the error message specified to error_handler.&lt;br /&gt;check_error() {&lt;br /&gt;  if [ $? -gt 0 ]&lt;br /&gt;  then&lt;br /&gt;    error_handler "$1"&lt;br /&gt;  fi&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;# Find all the files we downloaded, and unzip them, sending output to the logfile&lt;br /&gt;find $LOCAL_DIR/* -prune -name "$REMOTE_FILE_PATTERN" -exec unzip -d $LOCAL_DIR {} &amp;gt;&amp;gt; $LOGFILE \;&lt;br /&gt;check_error "unzip failed"&lt;br /&gt;&lt;br /&gt;# Output from unzip will contain either "inflating" or "extracting" for each file in the archive.&lt;br /&gt;# Look for these messages in the log file, find the filename part, and iterate over them&lt;br /&gt;for file in `grep -e inflating: -e extracting: $LOGFILE | awk '{print $2}'`&lt;br /&gt;do&lt;br /&gt;  # Run the SMB program to send the file to the Windows share...&lt;br /&gt;  $SMB_CMDLINE ToShare $file $SMB_SERVER $SMB_PATH&lt;br /&gt;  check_error "Failed to copy $file to $SMB_SERVER"&lt;br /&gt;  # ...and then delete it. This is fine because the original zipped files are untouched.&lt;br /&gt;  rm $file&lt;br /&gt;  check_error "Failed to delete $file"&lt;br /&gt;done&lt;br /&gt;&lt;br /&gt;# Next, do the same thing for the zipped files themselves...&lt;br /&gt;for file in `find $LOCAL_DIR/* -prune -name "$REMOTE_FILE_PATTERN"`&lt;br /&gt;do&lt;br /&gt;  $SMB_CMDLINE ToShare $file $SMB_SERVER $SMB_PATH&lt;br /&gt;  check_error "Failed to copy $file to $SMB_SERVER"&lt;br /&gt;  # ...except just move the files to an archive directory&lt;br /&gt;  mv $file $LOCAL_ARCHIVE_DIR&lt;br /&gt;check_error "Failed to archive $file"&lt;br /&gt;done&lt;br /&gt;logit "Files successfully copied to $SMB_SERVER, moving files on remote server to archive directory"&lt;/pre&gt;&lt;br /&gt;After adding this functionality, I was cursing my desire to do this in shell instead of writing a real program. But as these things go, the project's timeline was short, and starting over was out of the question. Plus, as I'm wont to do, I gave a lot of assurances that a file transfer script was a no-brainer before I had all the facts - one of the facts being that the vendor was using ftps, not sftp as advertised, the whole reason I chose this script template in the first place.&lt;br /&gt;&lt;br /&gt;Third, the vendor requested that after downloading files, that they be moved to an archive directory on the vendor's ftps server. Normally it's polite to delete a file after you download it from a vendor, as it saves them space requirements, and can also let them know at a glance what files have been pulled. Moving a file to another directory is asked for rarely, but usually isn't a problem. For this process, however, multiple files would be available at once, all of which had timestamps in the filename instead of fixed names. A tolerable approach would have been to download and delete using mget and mdel, but there is no facility in standard ftp clients for batch renames on the remote server.&lt;br /&gt;&lt;br /&gt;Had I started with perl, I could have played with named pipes and searching logfiles for filenames to build a set of rename commands to print to the pipe. Had I started with Java, I could have used the Glub bean and managed the ftps session on the fly, getting a file list and iterating over it with RETR and RNFR/RNTO commands. And there wasn't time to do either. And the Unix "expect" program wasn't installed, so I couldn't manage the ftps session that way. No, I was unfortunately going to have to do this in &lt;b&gt;two&lt;/b&gt; sessions. Workable, but ugly, and it's generally considered bad mojo to log on to a server twice in the same process. But that's what I did.&lt;br /&gt;&lt;br /&gt;Fortunately, Glub logs all the server RETR commands, giving me something simple to grep for with which to build a list:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;# Build the multiple rename (mren) string. For each logfile line starting with "RETR",&lt;br /&gt;# print out a corresponding line that says "rename (file) archive/(file)", save as $MREN&lt;br /&gt;export MREN=`perl -lne 'if (/^RETR /){s!RETR (.+)!rename $1 $ENV{REMOTE_ARCHIVE_DIR}/$1!;print}' $LOGFILE`&lt;br /&gt;&lt;br /&gt;# Log on to remote ftps server, execute $MREN lines&lt;br /&gt;$JAVA -Dglub.user.dir=$HOME -Duser.dir=$FTPS -jar $FTPS/secureftp2.jar &amp;lt;&amp;lt;EOF&lt;br /&gt;log $LOGFILE&lt;br /&gt;open $FTPS_HOST $FTPS_PORT&lt;br /&gt;user $FTPS_USER $FTPS_PASS&lt;br /&gt;cd $REMOTE_DIR&lt;br /&gt;$MREN&lt;br /&gt;bye&lt;br /&gt;EOF&lt;br /&gt;check_error "ftps archive session failed"&lt;br /&gt;&lt;br /&gt;logit "Session successful"&lt;/pre&gt;&lt;br /&gt;So eventually I'll be able to circle around and write this in a more sensible manner, or better yet, convince the vendor that they're better off if I just delete the files, and the Windows people that unzipping is really a breeze. The good news to all this madness is that I added about 10% to my commandline Unix bag of tricks, and you really can do a lot of neat stuff with shell scripts.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11885757-2096171650018831301?l=cautery.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://cautery.blogspot.com/feeds/2096171650018831301/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://cautery.blogspot.com/2010/10/korn-shell-hack-of-day.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11885757/posts/default/2096171650018831301'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11885757/posts/default/2096171650018831301'/><link rel='alternate' type='text/html' href='http://cautery.blogspot.com/2010/10/korn-shell-hack-of-day.html' title='Korn shell hack of the day'/><author><name>Curtis Autery</name><uri>https://profiles.google.com/107677530285177731535</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh4.googleusercontent.com/--J3C_khtLqQ/AAAAAAAAAAI/AAAAAAAADT4/e2Qa5ncfAMk/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11885757.post-638561017284763313</id><published>2010-10-03T08:13:00.003-04:00</published><updated>2010-10-03T08:23:39.061-04:00</updated><title type='text'>Picnic with the missus</title><content type='html'>Liberty got this cool wicker picnic basket on eBay a while ago, and we decided to take it out for a spin yesterday. One of the better side effects of "bill week" (when your bills all come due at once, and your budget spreadsheet looks a little scary) is that you can find fun money savers that double as relationship builders.&lt;br /&gt;&lt;br /&gt;Instead of spending $20 on a movie and snacks just to sit in the dark and be quiet, let's go out for a walk in the sunshine and talk to each other... for free. Instead of stopping for greasy fast food somewhere, or spending more at a better restaurant, let's cook up some food for lunch, and take it with us somewhere. In this case, "some food" was steak cut into strips and fried up with pepper, sweet Italian mini bread, cheddar cheese cut into slices, some green onion, and a refreshing carbonated beverage (the basket has a cool separate area for bottles), and "somewhere" was Highbanks park.&lt;br /&gt;&lt;br /&gt;When we got there, we found a long trail that didn't seem to have a lot of people on it, and proceeded to hike around looking for unofficial exit points: Good places to hang out that are accessible, hidden from the trail, but without signs saying not to go there. What we found was here:&lt;br /&gt;&lt;br /&gt;&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;iframe frameborder="0" height="350" marginheight="0" marginwidth="0" scrolling="no" src="http://maps.google.com/maps?q=40.154587,-83.033053&amp;amp;num=1&amp;amp;t=h&amp;amp;sll=40.126174,-82.92907&amp;amp;sspn=0.068271,0.128059&amp;amp;ie=UTF8&amp;amp;ll=40.154589,-83.032949&amp;amp;spn=0.001367,0.00284&amp;amp;z=14&amp;amp;output=embed" width="425"&gt;&lt;/iframe&gt;&lt;br /&gt;&lt;small&gt;&lt;a href="http://maps.google.com/maps?q=40.154587,-83.033053&amp;amp;num=1&amp;amp;t=h&amp;amp;sll=40.126174,-82.92907&amp;amp;sspn=0.068271,0.128059&amp;amp;ie=UTF8&amp;amp;ll=40.154589,-83.032949&amp;amp;spn=0.001367,0.00284&amp;amp;z=14&amp;amp;source=embed" style="color: blue; text-align: left;"&gt;View Larger Map&lt;/a&gt;&lt;/small&gt;&lt;br /&gt;&lt;br /&gt;Although the spot wasn't visible from the main trail (Dripping Rock, I believe), another trail frequented with dog walkers came close to it, and there was an occasional group of walkers coming near enough to see us, had they been paying attention to their surroundings. I think a couple people noticed us, but most people walked right by, oblivious.&lt;br /&gt;&lt;br /&gt;At one point, a ranger came driving by in one of their little Jeeps, who was for all appearances looking for people hiding in the woods. He happened to turn his head the other way as he drove by - twice! Once on the way out from his station, once on the way back to it. Funny, but, I mean, come on dude! At any rate, we spread our blanket, ate our makeshift steak sandwiches, chatted, watched the clouds, and left the area as we found it - minus a few burrs, that is.&lt;br /&gt;&lt;br /&gt;While hiking and rescuing the occasional woolly worm, we also people-watched. We had higher hopes for our fellow man there. Since we were in the woods, people should be healthier and less mainstream, no? "The Game" was on, so clearly the people at the park that day shouldn't be uber-consumer sports fans, and they should mostly be healthier and not couch potatoes. So one would think, but not the case. No matter how far we got from the main non-trail area, we would still hear the occasional yawp of joy as some sort of praiseworthy action from the game was reported on the radio. For some the exercise of walking up the trail's first hill was clearly the most punishment they had given their body in some time. There was also the occasional person walking on the trail while talking on their cellphone, forgivable, but lame. One of the families walking had an indifferent dad who busied himself with looking at trees or fields, making sure to keep himself a few feet away from his wife and kids. Why are you out with your family, exactly? If you don't like them, cut the cord now and lessen the pain on everyone. Don't put on the thin facade of just being stoic when your family can feel the vacuum where your love is supposed to be. Asshole. That part was the worst.&lt;br /&gt;&lt;br /&gt;All was not lost, though. Most of the groups were clearly happy to be both together and out trekking through the woods. Most of the people were, indeed, healthy. There was this 50-something woman running with a practiced pace, listening to her iPod, and looking like she could confidently stave off attacks from local bears or mountain lions... I'm fairly certain we have neither, but still, when the fight goes down, I want granny on my side.&lt;br /&gt;&lt;br /&gt;Lastly, there was a family whose mom or eldest daughter (I couldn't tell her age very well) was clearly recovering from cancer treatments, as she had the telltale wig, missing eyebrows, and extra lipstick, and was more fatigued than the rest of her group. But still out building up strength, building strong family bonds, and looking forward. When you hike the trail despite feeling bad, you're looking forward. When you let your family bring you to the park for the "fresh air" and just sit at a picnic table, you're waiting to die. She was looking forward, as was her family, who wasn't treating her as though she were fragile. That part was the best.&lt;br /&gt;&lt;br /&gt;We had a lot of fun on the cheap. Despite the few stinkers, most of the people ranged from tolerable to downright enjoyable. And although next weekend I should have some actual disposable income, maybe we'll just bank it and do something free and outside instead.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11885757-638561017284763313?l=cautery.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://cautery.blogspot.com/feeds/638561017284763313/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://cautery.blogspot.com/2010/10/picnic-with-missus.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11885757/posts/default/638561017284763313'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11885757/posts/default/638561017284763313'/><link rel='alternate' type='text/html' href='http://cautery.blogspot.com/2010/10/picnic-with-missus.html' title='Picnic with the missus'/><author><name>Curtis Autery</name><uri>https://profiles.google.com/107677530285177731535</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh4.googleusercontent.com/--J3C_khtLqQ/AAAAAAAAAAI/AAAAAAAADT4/e2Qa5ncfAMk/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11885757.post-1143739936616131668</id><published>2010-09-30T06:27:00.003-04:00</published><updated>2010-09-30T06:38:39.392-04:00</updated><title type='text'>Bezier Curves as Ellipses</title><content type='html'>A couple weeks ago, I was exploring the source code for the Java runtime classes, trying to figure out exactly how Java draws a circle, and was surprised at what I found. A circle can be defined as an instance of the java.awt.geom.Ellipse2D class with equal width and height. Graphics2D attempts to draw the Ellipse2D shape by calling for its PathIterator. A PathIterator returns segments of the shape in the form of SEG_MOVETO, SEG_LINETO, SEG_QUADTO (quadratic Bezier curve), SEG_CUBICTO (cubic Bezier curve), or SEG_CLOSE. An Ellipse2D contains an initial MOVETO, four CUBICTO segments, and a final CLOSE.&lt;br /&gt;&lt;br /&gt;I eventually got my head around what that meant and its implication: The standard 2D graphics library doesn't concern itself with center points and radii, or foci, or pi. In fact, it doesn't even render a &lt;em&gt;real&lt;/em&gt; circle, but a very close approximation. It renders four cubic Bezier curves representing 90 degree arcs of the circle. I found this discovery strange at the time, but on reflection it made sense.&lt;br /&gt;&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;First, turning the radius and angle into Cartesian coordinates requires using sine and cosine functions. Java doesn't contain its own sine calculator or lookup table, it delegates to a native library. Second, once all the Bezier control points are established, no expensive division or square roots need to be done in order to calculate the curve. Only addition, subtraction, and multiplication. Third, while conic sections can all be represented by Bezier curves, the reverse is not true, so if you had to pick one, Bezier would be it. Lastly, applying affine transforms to curves is as easy as applying the transforms to the control points and redrawing. So basically these curves look the same to the naked eye as a circle, and are easier on the processor to render. Whether or not that's why the Java developers went that direction, I have no idea.&lt;br /&gt;&lt;br /&gt;So, what's a Bezier curve, anyway?&lt;br /&gt;&lt;br /&gt;A Quadratic Bezier curve is a distortion to a straight line with a single "control point" that has influence over it, acting like a sort of gravity well. The closer a point on the line would be to the control point, the more the curve is distorted from the line.&lt;br /&gt;&lt;br /&gt;Plotting the curve is surprisingly simple. Start with a line between the start and control points, and another between the control and end points.&lt;br /&gt;&lt;br /&gt;&lt;img src="http://lh5.ggpht.com/_YH1HTjXGzNU/TKO-CtF73wI/AAAAAAAADF0/fCm6KB5WMNk/s800/BezierPre.PNG" /&gt;&lt;br /&gt;&lt;br /&gt;Next, pick a percentage, that we'll call "t". Plot a point t% between start and control, and another t% between control and end. Draw a line between these points. On this ancillary line, the point at t% gets added to the curve.&lt;br /&gt;&lt;br /&gt;That's it. Repeat this for all possible "t"s, and you have your curve. Below is a simplified example, showing t at 25%, 50%, and 75%:&lt;br /&gt;&lt;br /&gt;&lt;img src="http://lh6.ggpht.com/_YH1HTjXGzNU/TKO-C70LofI/AAAAAAAADF4/d6wBNwexzDs/s800/Bezier25.PNG" /&gt; &lt;img src="http://lh5.ggpht.com/_YH1HTjXGzNU/TKO-DGZNlQI/AAAAAAAADF8/Gy7GGeqhTgg/s800/Bezier50.PNG" /&gt;&lt;br /&gt;&lt;img src="http://lh3.ggpht.com/_YH1HTjXGzNU/TKO-DpTpQjI/AAAAAAAADGA/OTPFGKtX5oc/s800/Bezier75.PNG" /&gt; &lt;img src="http://lh3.ggpht.com/_YH1HTjXGzNU/TKO-DgIdr8I/AAAAAAAADGE/TulLsJiJMxA/s800/Bezier100.PNG" /&gt;&lt;br /&gt;&lt;br /&gt;This shows the same curve at 5% iterations instead of in quarters, showing more detail of the curved shape:&lt;br /&gt;&lt;br /&gt;&lt;img src="http://lh5.ggpht.com/_YH1HTjXGzNU/TKPBn5Se04I/AAAAAAAADGM/tgclboPX14g/s800/BezierPoints.PNG" /&gt;&lt;br /&gt;&lt;br /&gt;A Cubic Bezier curve functions on the same basic principle, except there is an additional control point distorting the curve. Plotting them out is similar, but you have two ancillary lines instead of one, which you draw an... ancillarier? tertiary? line between, as illustrated here:&lt;br /&gt;&lt;br /&gt;&lt;img src="http://lh6.ggpht.com/_YH1HTjXGzNU/TKPPuKQaQ4I/AAAAAAAADGU/H_zCQ_KvtO4/s800/Cubic25.PNG" /&gt; &lt;img src="http://lh5.ggpht.com/_YH1HTjXGzNU/TKPPuP0pAWI/AAAAAAAADGY/YbiIhWiZ6y8/s800/Cubic50.PNG" /&gt; &lt;img src="http://lh4.ggpht.com/_YH1HTjXGzNU/TKPPuQOToqI/AAAAAAAADGc/PxMTRk_88TQ/s800/Cubic75.PNG" /&gt;&lt;br /&gt;&lt;br /&gt;You can make fancier shapes with a cubic curve, such as moving one control point on the opposite side of the start/end line to make an s-shape:&lt;br /&gt;&lt;br /&gt;&lt;img src="http://lh4.ggpht.com/_YH1HTjXGzNU/TKRY0Z520kI/AAAAAAAADGo/s5ML0k3gN3M/s800/CubicS.PNG" /&gt;&lt;br /&gt;&lt;br /&gt;Neat, but doesn't help us draw a circle. What does, however, is the equation for kappa, &lt;img src="http://lh5.ggpht.com/_YH1HTjXGzNU/TKRgWD1sZUI/AAAAAAAADG4/0mR9rZ9oMfA/s800/kappa.png" /&gt;, which G. Adam Stanislav derives for us &lt;a href="http://www.whizkidtech.redprince.net/bezier/circle/kappa/"&gt;here&lt;/a&gt;. If the start and end points are on opposite corners of a square, and control points are on perpendicular lines, each kappa * side away from a termination point, a circular arc is drawn:&lt;br /&gt;&lt;br /&gt;&lt;img src="http://lh3.ggpht.com/_YH1HTjXGzNU/TKRgWFkCSqI/AAAAAAAADGw/lPeKcawzweE/s800/arc50.PNG" /&gt; &lt;img src="http://lh3.ggpht.com/_YH1HTjXGzNU/TKRgWMtKrKI/AAAAAAAADG0/k_dT3yfFzUo/s800/arc100.PNG" /&gt;&lt;br /&gt;&lt;br /&gt;...which is the way Java does it. The equation for kappa evaluates to roughly .5522847498307933, which Java has as a static variable in the java.awt.geom.EllipseIterator class, to avoid needing to calculate a square root every time a circle is drawn.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;public static final double CtrlVal = 0.5522847498307933;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11885757-1143739936616131668?l=cautery.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://cautery.blogspot.com/feeds/1143739936616131668/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://cautery.blogspot.com/2010/09/bezier-curves-as-ellipses.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11885757/posts/default/1143739936616131668'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11885757/posts/default/1143739936616131668'/><link rel='alternate' type='text/html' href='http://cautery.blogspot.com/2010/09/bezier-curves-as-ellipses.html' title='Bezier Curves as Ellipses'/><author><name>Curtis Autery</name><uri>https://profiles.google.com/107677530285177731535</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh4.googleusercontent.com/--J3C_khtLqQ/AAAAAAAAAAI/AAAAAAAADT4/e2Qa5ncfAMk/s512-c/photo.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh5.ggpht.com/_YH1HTjXGzNU/TKO-CtF73wI/AAAAAAAADF0/fCm6KB5WMNk/s72-c/BezierPre.PNG' height='72' width='72'/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11885757.post-8487627563468363037</id><published>2010-09-28T14:33:00.000-04:00</published><updated>2010-09-28T14:33:20.802-04:00</updated><title type='text'>Form validation using custom attributes</title><content type='html'>&lt;blockquote&gt;&lt;b&gt;Background&lt;/b&gt;&lt;/blockquote&gt;&lt;br /&gt;A couple months ago, a friend's daughter came to me for help with a website she was writing for a diabetes seminar registration page. The input form needed to ask the user if they had diabetes, and if so, whether it was type 1 or type 2. She was using Dreamweaver, and was having difficulty getting the form to behave correctly with Spry Widgets form validation.&lt;br /&gt;&lt;br /&gt;I wasn't familiar with Spry Widgets (or Dreamweaver, for that matter), so I asked her to show me the source code. We hacked out a semi-functional solution in a few minutes, but I wasn't very happy with it. Spry had input validation that intercepted form submit events. You defined some JavaScript objects declaring what input elements were required, and what format they had to be in (e.g., the input must look like a phone number, or it must look like an email address). When you submitted the form, anything that was wrong gets highlighted red, and you have to go back and correct it.&lt;br /&gt;&lt;br /&gt;The definition syntax for a single field looked like this:&lt;br /&gt;&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;&lt;pre&gt;&amp;lt;span id="phoneA"&amp;gt;Primary Contact Number:&lt;br /&gt;&amp;lt;input type="text" name="primary" id="primary" /&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;&amp;lt;script&amp;gt;var phoneA = new Spry.Widget.ValidationTextField("phoneA", "phone_number");&amp;lt;/script&amp;gt;&lt;/pre&gt;&lt;br /&gt;Messy, but I was prepared to accept it if I could make it do what I wanted. Unfortunately, I couldn't. I couldn't make Spry validate the diabetes type only if "yes" was selected on the "do you have diabetes" question. I also couldn't write my own onsubmit handler, as it would interfere with Spry's, and it was unclear where the entry point to its validation was located, as it was, I don't mind saying, arbitrarily complicated madness, not fit for distribution outside of a Lovecraft novel. OK, maybe that's a little harsh. But I didn't like it.&lt;br /&gt;&lt;br /&gt;We settled on leaving the "type" selector disabled, and adding an "onchange" handler for the "do you have diabetes" selector that enabled it, defaulting to type 1, and hoping the registree would notice and change that if needed. It was functional enough for our purposes, but like all the solutions I write that look sloppy, it weighed on me.&lt;br /&gt;&lt;br /&gt;I decided there was nothing for it but to re-write form validation from scratch, and abandon Spry. And so I did, along the way making a formal form validation system that could be imported as a JavaScript file and used with much simpler syntax than Spry. Additionally, I added a phone number formatter (entering 614.555.1212 would autocorrect to (614) 555-1212), a section duplicator (if you want a form that lets you add users, or more items to a cart, for example), and a "validate as you go" feature where tabbing off of an input field causes that field to be quick-checked.&lt;br /&gt;&lt;br /&gt;My goal was to separate validation functions as much as possible from the HTML for the form itself, much like CSS separates layout design from content. The mechanism I used was simple custom attributes that could be inserted into input elements. Here's an example of a small form importing and using my validation script:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&amp;lt;!doctype html&amp;gt;&lt;br /&gt;&amp;lt;html&amp;gt;&amp;lt;head&amp;gt;&lt;br /&gt;  &amp;lt;script src="http://sites.google.com/site/ceauterytest/Home/caFormTools.js"&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;&amp;lt;/head&amp;gt;&amp;lt;body onload=init_caTools()&amp;gt;&lt;br /&gt;&amp;lt;form&amp;gt;&lt;br /&gt;  Please enter some people (up to 3):&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;  &amp;lt;div id=Person dup=1 add="Add a person" rem="Remove a person" max=3&amp;gt;&lt;br /&gt;    Person 1:&amp;lt;br /&amp;gt;&lt;br /&gt;    &amp;lt;input type=text name=phone placeholder="Phone Number" req=1 phn=1&amp;gt;&lt;br /&gt;    Phone number (w/area code)*&lt;br /&gt;    &amp;lt;br /&amp;gt;&lt;br /&gt;    &amp;lt;input type=text name=email placeholder="Email address"&amp;gt;Email address&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;  &amp;lt;/div&amp;gt;&lt;br /&gt;  &amp;lt;input type=submit value=Continue&amp;gt;&lt;br /&gt;&amp;lt;/form&amp;gt;&lt;br /&gt;(* = Required)&lt;br /&gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&lt;/pre&gt;&lt;br /&gt;This looks basically like any other HTML form, but with a suspicious &amp;lt;div&amp;gt; tag (which I'll get to later), and the use of the new HTML5 input attributes, "placeholder", and finally a single call to init_caTools to get things started.&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;&lt;b&gt;The rewrite in action&lt;/b&gt;&lt;/blockquote&gt;&lt;br /&gt;Here is what the above HTML looks like before any action happens. Notice the greyed out "Email address" in the second input field: This is from the "placeholder" attribute, new in HTML5, and supported by Chrome. Notice that the "Add a person" link is not declared in the HTML above, but matches the "add" attribute of the div tag. More on that later.&lt;br /&gt;&lt;br /&gt;In the phone number field, note that I have left off the area code, which should fail validation.&lt;br /&gt;&lt;br /&gt;&lt;img src="http://lh4.ggpht.com/_YH1HTjXGzNU/TAKLTkWPXaI/AAAAAAAAC_I/YFeUGpNFitI/s800/01-intro.PNG.jpg" /&gt;&lt;br /&gt;&lt;br /&gt;And it does! When I click "Continue", two things happen: An alert box opens describing the problem, and the input field in question gets highlighted red.&lt;br /&gt;&lt;br /&gt;&lt;img src="http://lh6.ggpht.com/_YH1HTjXGzNU/TAKLURHysgI/AAAAAAAAC_M/hWvJudnumHY/s800/02-badPhone.PNG.jpg" /&gt;&lt;br /&gt;&lt;br /&gt;After dismissing the dialog box, I have added an area code to the phone number, but my format is "614.555-1212", which has all the right elements, but uses a non-standard format.&lt;br /&gt;&lt;br /&gt;&lt;img src="http://lh4.ggpht.com/_YH1HTjXGzNU/TAKLUg8XqWI/AAAAAAAAC_Q/hJ6bIcJKI1s/s800/03-phoneFix_before.PNG.jpg" /&gt;&lt;br /&gt;&lt;br /&gt;...but when I click to another field, the format is changed to one that is more standard. My phone validator has two regular expressions. One checks for a perfect match of the format I'm looking for, and if that fails to match, a second formatter checks to see if there are two groups of three digits, and a group of 4, separated by whitespace, periods, dashes, and optionally the area code having parenthesis. If the numbers are in there, I grab them, and build a new string in the correct format.&lt;br /&gt;&lt;br /&gt;Note that not only did the number format get corrected, the field is no longer red. This wasn't a full form validation, just a quick check of one field.&lt;br /&gt;&lt;br /&gt;&lt;img src="http://lh5.ggpht.com/_YH1HTjXGzNU/TAKLVJKet6I/AAAAAAAAC_U/_xMKltJDtho/s800/04-phoneFix_after.PNG.jpg" /&gt;&lt;br /&gt;&lt;br /&gt;In the next screenshot, I have clicked the "Add a person" link, left the second phone number blank, put an invalid address into the email address field, and clicked submit. The alert box tells me that I have left a required field blank, and that the email address isn't formatted correctly.&lt;br /&gt;&lt;br /&gt;In the "Person 1" section, the email address is blank, but that passes validation since I have not also marked the field required. So that field is basically saying "you can leave me blank, but if you don't, put something in that makes sense."&lt;br /&gt;&lt;br /&gt;Also notice the "Remove a person" link has appeared now that there is more than one person.&lt;br /&gt;&lt;br /&gt;&lt;img src="http://lh6.ggpht.com/_YH1HTjXGzNU/TAKLVX1WaDI/AAAAAAAAC_Y/YqYnh3AeUQ0/s800/05-addPerson.PNG.jpg" /&gt;&lt;br /&gt;&lt;br /&gt;For the next screenshot, I have corrected all the errors, and added a third person, given a third phone number, and clicked "Continue" again. After that, validation passes, but the form does not yet get submitted.&lt;br /&gt;&lt;br /&gt;Instead, the screen gets "lightboxed" using a quick lightbox-ish hack, and a final verification prompt is given. Note that the blank fields aren't included in the prompt.&lt;br /&gt;&lt;br /&gt;&lt;img src="http://lh3.ggpht.com/_YH1HTjXGzNU/TAKLV0idunI/AAAAAAAAC_c/P9bw1M0MjsM/s800/06-verify.PNG.jpg" /&gt;&lt;br /&gt;&lt;br /&gt;Lastly, since the form had no declared action, it defaults to loading the same page again with the form data appended to the URL. Note here that the form data in the address bar skips from "phone" to "phone2" without the blank email address. The same routine that parses the blank fields out of the verification step disables the fields so they aren't included with the final form submission.&lt;br /&gt;&lt;br /&gt;&lt;img src="http://lh4.ggpht.com/_YH1HTjXGzNU/TAKLWJopfyI/AAAAAAAAC_g/-SmbIx9kvX0/s800/07-droppedBlank.PNG.jpg" /&gt;&lt;br /&gt;&lt;br /&gt;So, how does all this work? What follows is a lengthy explanation. Alternatively, you can scroll to the bottom for a link to the source code and a test page showing the validation code in action.&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;&lt;b&gt;The Basics: Custom attributes&lt;/b&gt;&lt;/blockquote&gt;&lt;br /&gt;HTML tags use attributes to define behavior, or what type of control will be rendered in the browser. For example the &amp;lt;input&amp;gt; tag defines attributes of type, name, value, size, and others. A text input field intended to receive phone numbers may be declared thusly:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&amp;lt;input type=text name=phone1&amp;gt;&lt;/pre&gt;&lt;br /&gt;A custom attribute can be inserted into a tag which is not defined in the HTML standard, which is perfectly legal, and causes nothing more in the browser than complete indifference. JavaScript can read these non-standard attributes using the element's getAttribute() method. For example, if this input field exists in an HTML document:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&amp;lt;input id=phoneNbr type=text name=phone1 req=1 phn=1&amp;gt;&lt;/pre&gt;&lt;br /&gt;...I can read the phn attribute with the following:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;document.getElementById("phoneNbr").getAttribute("phn")&lt;/pre&gt;&lt;br /&gt;This concept opens up a world of possibilities for form validation. Let's say that I want the "req" attribute to declare an input element to be required. A JavaScript function can then iterate through all the form elements and check for the attributes in question, and alert if anything is amiss with something like this:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;//el = the current element&lt;br /&gt;var name = el.placeholder || el.name; // Use the placeholder as the name, if it exists&lt;br /&gt;if (el.getAttribute('req') &amp;amp;&amp;amp; el.value == "") {&lt;br /&gt;  errorMsg = name + " is a required field.\n";&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;Each type of custom attribute can be checked for, and if found the field's value can be checked with a regular expression. If I want the "zip" attribute to indicate a zip code, I can check fields of that type with this:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;if (el.getAttribute('zip') &amp;amp;&amp;amp; !/^\d{5}$/.test(el.value)) {&lt;br /&gt;  errorMsg = name + " must be a zip code.\n";&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;I'm not going to go into a lengthy discussion of the regular expressions used in the final script, but I encourage you to learn regular expression syntax if you aren't familiar with it. They're a boon, and supported in many languages now. (The real zip code regex also checks for Zip+4, the one above is just a simple example to illustrate the point.)&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;&lt;b&gt;Validating all element types&lt;/b&gt;&lt;/blockquote&gt;&lt;br /&gt;I more fleshed out element validation routine is shown below. Both of our two types (phn and zip) are checked for, and some more expansive "required" logic is performed (specifically, the idea that if a field isn't required, it's allowed to be blank). For phone number matching, since I want to do my auto-correct function, I'm calling a new function, validatePhone, instead of just doing a regex check.&lt;br /&gt;&lt;br /&gt;The overall concept here is that the "errorMsg" string is dual purpose, serving as a flag and an error container. At the end, if errorMsg is populated, the background color of the element is set to the failedBG color.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;var passedBG = 'white';&lt;br /&gt;var failedBG = 'red';&lt;br /&gt;var zipMatch = /^\d{5}($|\s*[-\.]?\s*\d{4}$)/; // US Zipcode or zip+4&lt;br /&gt;var numMatch = /^\d+$/;&lt;br /&gt;var emailMatch = /^.+\@.+\..{2}.?.?$/;         // Lazy, but accounts for most&lt;br /&gt;function validateElem(el) {&lt;br /&gt;  var errorMsg = "";&lt;br /&gt;  var name = el.placeholder || el.name;&lt;br /&gt;  if (!el.getAttribute('req') &amp;amp;&amp;amp; el.value == "") { errorMsg = ""; }&lt;br /&gt;  else if (el.getAttribute('phn') &amp;amp;&amp;amp; !validatePhone(el)) {&lt;br /&gt;    errorMsg = name + " must be a phone number with area code.\n";&lt;br /&gt;  }&lt;br /&gt;  else if (el.getAttribute('zip') &amp;amp;&amp;amp; !zipMatch.test(el.value)) {&lt;br /&gt;    errorMsg = name + " must be a zip code or zip+4 code.\n";&lt;br /&gt;  }&lt;br /&gt;  else if ((el.type == 'email' || el.getAttribute('eml')) &amp;amp;&amp;amp; !emailMatch.test(el.value)) {&lt;br /&gt;    errorMsg = name + " must be a properly formatted email address.\n";&lt;br /&gt;  }&lt;br /&gt;  else if ((el.type == 'number' || el.getAttribute('num')) &amp;amp;&amp;amp; !numMatch.test(el.value)) {&lt;br /&gt;    errorMsg = name + " must contain a number.\n";&lt;br /&gt;  }&lt;br /&gt;  else if (el.getAttribute('req') &amp;amp;&amp;amp; el.value == "") {&lt;br /&gt;    errorMsg = name + " is a required field.\n";&lt;br /&gt;  }&lt;br /&gt;  if (errorMsg == "") { el.style.background = passedBG; }&lt;br /&gt;  else { el.style.background = failedBG; }&lt;br /&gt;  return errorMsg;&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;blockquote&gt;&lt;b&gt;Auto-correcting the phone number&lt;/b&gt;&lt;/blockquote&gt;&lt;br /&gt;Here we define a regular expression for exact (###) ###-#### phone numbers, and return success quickly if a match is found. Otherwise, we fail back to using the alternate regex, "phoneSimilar" (which looks crazy, and purists will hate my use of .* at the end, and the fact that 6 character TLDs aren't supported). If phoneSimilar matches, all the digits are collected and reformatted in a way that phoneMatch would accept.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;var phoneMatch = /^\(\d{3}\) \d{3}\-\d{4}$/;&lt;br /&gt;var phoneSimilar = /^\s*\(?(\d{3})\)?\s*[-\.]?\s*(\d{3})\s*[-\.]?\s*(\d{4}).*$/;&lt;br /&gt;function validatePhone(el) {&lt;br /&gt;  if(phoneMatch.test(el.value)) { return true; }&lt;br /&gt;  if(phoneSimilar.test(el.value)) {&lt;br /&gt;    el.value = el.value.replace(phoneSimilar, "($1) $2-$3");&lt;br /&gt;    return true;&lt;br /&gt;  }&lt;br /&gt;  return false;&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;blockquote&gt;&lt;b&gt;Validating the entire form&lt;/b&gt;&lt;/blockquote&gt;&lt;br /&gt;Now that we can validate elements, we need a routine to iterate through all of a form's elements. Rather than just validate each in turn and collect error messages, this function also keeps a list of blank elements that aren't required, and adds them to an array of elements to be disabled if validation succeeds. (More on that in part 2)&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;function formValidate(frm) {&lt;br /&gt;  for (var e = 0; e &amp;lt; frm.elements.length; e++) {&lt;br /&gt;    var el = frm.elements[e];&lt;br /&gt;    var msg = validateElem(el);&lt;br /&gt;    if (msg == "" &amp;amp;&amp;amp; el.value == "") { elemsToDisable.push(el); }&lt;br /&gt;    formErrors += msg;&lt;br /&gt;  }&lt;br /&gt;  if (formErrors == "") { return true; }&lt;br /&gt;  alert ("Please correct the following fields\n" + formErrors);&lt;br /&gt;  return false;&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;blockquote&gt;&lt;b&gt;Intercepting onsubmit programmatically&lt;/b&gt;&lt;/blockquote&gt;&lt;br /&gt;I don't want users of this script to modify anything in their form other than adding attributes to the input fields. Rather than require users to also add an "onsubmit=formValidate(this)" on each form, the script iterates through all of a document's forms, and programmatically alters their onsubmit handler.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;for (var f = 0; f &amp;lt; document.forms.length; f++) {&lt;br /&gt;  document.forms[f].onsubmit =  function onsubmit() { return formValidate(this) };&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;blockquote&gt;&lt;b&gt;onblur: Validate as you go&lt;/b&gt;&lt;/blockquote&gt;&lt;br /&gt;As shown above in the screenshots, element validations occur as you click from element to element. This is done via the handy "onblur" handler, which fires when an element loses focus (by clicking somewhere else, or tabbing to another field). These can be added individually to input elements like so:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&amp;lt;input type=text name=... onblur=validateElem(this)&amp;gt;&lt;/pre&gt;&lt;br /&gt;...but as with the form onsubmits, I'd rather have my script go out and find all the elements and set the onblur programmatically. Here is the iterator function from above, this time adding onblur functionality as well:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;for (var f = 0; f &amp;lt; document.forms.length; f++) {&lt;br /&gt;  var frm = document.forms[f];&lt;br /&gt;  frm.onsubmit =  function onsubmit() { return formValidate(this) };&lt;br /&gt;&lt;br /&gt;  // Iterate through each element, set it's onblur&lt;br /&gt;  for (i = 0; i &amp;lt; frm.elements.length; i++ ) {&lt;br /&gt;    frm.elements[i].onblur = function onblur() { validateElem(this) };&lt;br /&gt;  }&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;The "Sign up for Diabetes camp" page I was helping modify allowed you to register up to 4 adults and 4 kids in your party. Spry Widgets attacked this problem in the form of collapsed sections of the page. "Collapsed", in this case, meant hidden, with a header section you could click to make the hidden elements visible. Click the header on Adult 2 if there is a second adult, and up it springs. Boing!&lt;br /&gt;&lt;br /&gt;Unfortunately, all the hidden elements were still enabled on the form, meaning when the form is submitted they show up as blank inputs to be handled on the backend, which in this case was only formmail (don't get me started - unfortunately I was just being consulted for a quick hack on the validation, not to design the end-to-end process). The end result is that the site owner would get emails containing blank fields for all the unused collapsed sections every time a form was submitted. Every time. With no verification step.&lt;br /&gt;&lt;br /&gt;So, two problems to solve: More intelligent handling of additional sections users can add to a form, and some method of confronting the user with what he's about to do, to give him a chance to fix errors.&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;&lt;b&gt;Extending a form&lt;/b&gt;&lt;/blockquote&gt;&lt;br /&gt;My thinking was that it would be better to not have the "collapsed" elements in the HTML at all, but instead have an "add additional person" link that would use JavaScript to create new input tags as needed. A quick Google search on the matter brought me to &lt;a href="http://www.quirksmode.org/dom/domform.html"&gt;this page&lt;/a&gt; on PPK's quirksmode.org site, which was a great introduction to programmatically extending forms and various problems you can run into. (In fact, PPK is someone I hold in high regard. I've had numerous unrelated web coding searches point me to his page, and always found the information therein useful.)&lt;br /&gt;&lt;br /&gt;PPK suggested the JavaScript "cloneNode" approach was the way to go. What I wanted was a regular expression search/replace based on the name of the section you wanted to clone. Say the cloneable section was named "Person". I wanted every occurrence of "Person 1" in the text of the section to be replaced in the duplicate with "Person 2", then "Person 3" for the next addition, etc. The form input names would all keep count as well. An input field named "phone" should become "phone2", then "phone3". The problem was, every time I used JavaScript's cloneNode function and then tried to touch the raw HTML of the new node, bad things would happen. In IE8, the node's innards would disappear completely. Not the behavior I was looking for.&lt;br /&gt;&lt;br /&gt;I instead wrote a framework using a closure to hold all the innerHTMLs of div nodes that contained an attribute of "dup". When encountering those, further attributes of "add" and "rem" are looked for to determine what the UI text for add and remove links should look like. Lastly, the "max" attribute is looked for to see when to stop letting the user add more nodes. Consider the following HTML:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&amp;lt;!doctype html&amp;gt;&lt;br /&gt;&amp;lt;html&amp;gt;&amp;lt;head&amp;gt;&lt;br /&gt;  &amp;lt;script src="http://sites.google.com/site/ceauterytest/Home/caFormTools.js"&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;&amp;lt;/head&amp;gt;&amp;lt;body onload=init_caTools()&amp;gt;&lt;br /&gt;&amp;lt;form&amp;gt;&lt;br /&gt;  Please enter some people (up to 3):&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;&lt;strong&gt;  &amp;lt;div id=Person dup=1 add="Add a person" rem="Remove a person" max=3&amp;gt;&lt;/strong&gt;&lt;br /&gt;    Person 1:&amp;lt;br /&amp;gt;&lt;br /&gt;    &amp;lt;input type=text name=phone placeholder="Phone Number" req=1 phn=1&amp;gt;&lt;br /&gt;    Phone number (w/area code)*&lt;br /&gt;    &amp;lt;br /&amp;gt;&lt;br /&gt;    &amp;lt;input type=text name=email placeholder="Email address"&amp;gt;Email address&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;&lt;strong&gt;  &amp;lt;/div&amp;gt;&lt;/strong&gt;&lt;br /&gt;  &amp;lt;input type=submit value=Continue&amp;gt;&lt;br /&gt;&amp;lt;/form&amp;gt;&lt;br /&gt;(* = Required)&lt;br /&gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&lt;/pre&gt;&lt;br /&gt;The form framework will find the boldface line above, and use the add= and rem= text to build links to the custom clone function. The links are then inserted into the page just below where the boldface  closes the HTML block. The effect looks like this:&lt;br /&gt;&lt;br /&gt;&lt;img src="http://lh5.ggpht.com/_YH1HTjXGzNU/TBeKhfegVgI/AAAAAAAADAQ/52ay1TBqgdg/s800/08-dupAdd1.PNG.jpg" /&gt;&lt;br /&gt;&lt;br /&gt;(In the above screenshot, I've mouse-overed... moused-over?... the add link to show the href that is generated.) Clicking the link clones the&lt;br /&gt;&lt;br /&gt;block, substituting count numbers where appropriate, and then unhides the "remove" link:&lt;br /&gt;&lt;br /&gt;&lt;img src="http://lh6.ggpht.com/_YH1HTjXGzNU/TBeKh98jGhI/AAAAAAAADAU/MWocCZUAGug/s800/09-dupAdd2.PNG.jpg" /&gt;&lt;br /&gt;&lt;br /&gt;(Here I've moused-over the remove link to show its href). Lastly, when you get to the maximum number of nodes, the add link is hidden. If for some reason the link is still visible, the cloning code traps for the count separately. Said code, in all its nakedness, with some explanatory comments:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;function dupClosures() {&lt;br /&gt;  var inners = new Array(); // Initial .innerHTML of duplicatable div sections&lt;br /&gt;  var counts = new Array(); // How many of each duplicatable sections exist&lt;br /&gt;  var divs = document.getElementsByTagName('div'); // All the &amp;lt;div&amp;gt; blocks on the page&lt;br /&gt;&lt;br /&gt;  // Iterate through all the &amp;lt;div&amp;gt; blocks.&lt;br /&gt;  for (var f = 0; f &amp;lt; divs.length; f++) {&lt;br /&gt;    if (divs[f].getAttribute('dup')) { // If this is a duplicatable block&lt;br /&gt;      var id = divs[f].id;             // Get the id attribute&lt;br /&gt;      inners[id] = divs[f].innerHTML;  // Record raw HTML, give a starting count of 1&lt;br /&gt;      counts[id] = 1;                  // (Yes, you can use JS arrays like hashes)&lt;br /&gt;      // create a new &amp;lt;div&amp;gt; block in memory, but don't add it to the page&lt;br /&gt;      var suffix = document.createElement('div');&lt;br /&gt;      suffix.id = id + 'Dup'; // Set its ID&lt;br /&gt;      // Build the links to the javascript add and remove functions&lt;br /&gt;      suffix.innerHTML = "&amp;lt;span class=add id='" + id + "Add'&amp;gt;"&lt;br /&gt;       + "&amp;lt;a href=\"javascript:dupAdd('" + id + "')\"&amp;gt;"&lt;br /&gt;       + divs[f].getAttribute('add') + "&amp;lt;/a&amp;gt;&amp;lt;/span&amp;gt; "&lt;br /&gt;       + "&amp;lt;span class=rem style=display:none id='" + id + "Rem'&amp;gt;"&lt;br /&gt;       + " &amp;lt;a href=\"javascript:dupRem('" + id + "')\"&amp;gt;"&lt;br /&gt;       + divs[f].getAttribute('rem') + "&amp;lt;/a&amp;gt;&amp;lt;/span&amp;gt;";&lt;br /&gt;     /* Tricky insertBefore syntax. Add a new child to divs[f]'s parent, just before&lt;br /&gt;        the DOM element that comes after divs[f]. We don't want to just add the links as&lt;br /&gt;        children of the block to be duplicated, we want the duplicates to go there, and&lt;br /&gt;        the add/remove links to appear after all the duplicates */&lt;br /&gt;     divs[f].parentNode.insertBefore(suffix, divs[f].nextSibling);&lt;br /&gt;   }&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  dupAdd = function(id) {&lt;br /&gt;    var base = document.getElementById(id);&lt;br /&gt;&lt;br /&gt;    // Get maximum number attribute of object, exit if we've reached it, else increment&lt;br /&gt;    var max = base.getAttribute('max') || 2;&lt;br /&gt;    if (counts[id] &amp;gt;= max) { return; }&lt;br /&gt;    counts[id]++;&lt;br /&gt;&lt;br /&gt;/*  Create a new div block, set it's HTML content to the inners[id] set previously.&lt;br /&gt;    Replace instances of the ID with the ID followed by the current count.&lt;br /&gt;    e.g., If the ID is "Adult", the following transforms will take place:&lt;br /&gt;    "Adult" and "Adult1" become "Adult2"&lt;br /&gt;    "Adult " and "Adult 1" become "Adult 2" */&lt;br /&gt;&lt;br /&gt;    var child = document.createElement('div');&lt;br /&gt;    child.id = id + counts[id];&lt;br /&gt;    var re = new RegExp( "(" + id + "\\s?)1?", "g");&lt;br /&gt;    /* In perl speak, the above regex would be /($id\s?)1?/g&lt;br /&gt;       which means find the list of all strings that contain the id in question,&lt;br /&gt;       possibly followed by a space, and possibly followed by a 1. Remember everything&lt;br /&gt;       within the parenthesis and the entire match separately */&lt;br /&gt;    // Replace matches with what was in the parenthesis, plus the current count&lt;br /&gt;    child.innerHTML = inners[id].replace(re, "$1" + counts[id]);&lt;br /&gt;&lt;br /&gt;/*  Catch any input elements that didn't get replaced by the above, and append the&lt;br /&gt;    current count to their name attribute */&lt;br /&gt;&lt;br /&gt;    var nodes = child.getElementsByTagName("*"); // Get a list of all elements in "child"&lt;br /&gt;    for (var f = 0; f &amp;lt; nodes.length; f++) {&lt;br /&gt;      setOnBlur(nodes[f]);&lt;br /&gt;      if(nodes[f].name &amp;amp;&amp;amp; nodes[f].name.indexOf(id) == -1) {&lt;br /&gt;        nodes[f].name += counts[id];&lt;br /&gt;      }&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    // Append the new div block to the live document&lt;br /&gt;    base.appendChild(child);&lt;br /&gt;    // Unhide the remove link, and see if the add link should now be hidden&lt;br /&gt;    document.getElementById(id + 'Rem').style.display = 'inline';&lt;br /&gt;    if (counts[id] == max) {&lt;br /&gt;      document.getElementById(id + 'Add').style.display = 'none';&lt;br /&gt;    }&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;/* The remove function is much simpler. No regex or iterating through form elements,&lt;br /&gt;   just remove the last child you added to the DOM from above, and manage the add and&lt;br /&gt;   remove links as needed */&lt;br /&gt;&lt;br /&gt;  dupRem = function(id) {&lt;br /&gt;    if (counts[id] &amp;lt;= 1) { return; }&lt;br /&gt;    var base = document.getElementById(id);&lt;br /&gt;    base.removeChild(base.lastChild);&lt;br /&gt;&lt;br /&gt;    counts[id]--;&lt;br /&gt;    document.getElementById(id + 'Add').style.display = 'inline';&lt;br /&gt;    if (counts[id] == 1) {&lt;br /&gt;      document.getElementById(id + 'Rem').style.display = 'none';&lt;br /&gt;    }&lt;br /&gt;  }&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;blockquote&gt;&lt;b&gt;Lightbox verification window&lt;/b&gt;&lt;/blockquote&gt;&lt;br /&gt;If you aren't familiar with Lightbox, it's a completely obvious and elegant UI technique that is currently pretty poopular. It basically "greys out" a page, creates a modal dialog box that the user must interact with before returning to the page. It is commonly used for images: Click on a thumbnail, and the full-sized image is displayed on the same page in a modal that you can then close, and return to the main page.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://www.lokeshdhakar.com/projects/lightbox2/"&gt;This link&lt;/a&gt; goes to the current version of the Lightbox project, and it is worth looking at (or at least worth playing with the sample pages), however it isn't what I used; I just achieved a similar effect with a couple div tags with z-indices, and building a table of form element names and their values. The final effect looks like this:&lt;br /&gt;&lt;br /&gt;&lt;img src="http://lh3.ggpht.com/_YH1HTjXGzNU/TAKLV0idunI/AAAAAAAAC_c/P9bw1M0MjsM/s800/06-verify.PNG.jpg" /&gt;&lt;br /&gt;&lt;br /&gt;Above, I showed this simple form validation function:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;function formValidate(frm) {&lt;br /&gt;  for (var e = 0; e &amp;lt; frm.elements.length; e++) {&lt;br /&gt;    var el = frm.elements[e];&lt;br /&gt;    var msg = validateElem(el);&lt;br /&gt;    if (msg == "" &amp;amp;&amp;amp; el.value == "") { elemsToDisable.push(el); }&lt;br /&gt;    formErrors += msg;&lt;br /&gt;  }&lt;br /&gt;&lt;strong&gt;  if (formErrors == "") { return true; }&lt;/strong&gt;&lt;br /&gt;  alert ("Please correct the following fields\n" + formErrors);&lt;br /&gt;  return false;&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;The boldface line says that if there are no errors, return "true" to the caller, which is the form's onsubmit handler. For example:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&amp;lt;form action="/cgi-bin/doWhatever.cgi" onsubmit="return formValidate(this)"&amp;gt;&lt;/pre&gt;&lt;br /&gt;When the user clicks the submit button, formValidate is called first. If it returns true, the form data is submitted to doWhatever.cgi; if it returns false, the submit event is canceled. The final version of formValidate modifies the boldface line above to the following:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;if (formErrors == "") {&lt;br /&gt;    setPrompt(promptHeader + promptItems + promptTrailer);&lt;br /&gt;    openPromptBox();&lt;br /&gt;    return false;&lt;br /&gt;  }&lt;/pre&gt;&lt;br /&gt;Now formValidate will always return false, so the form will not get submitted. If I didn't do this, displaying the "are you sure" dialog would be followed immediately by the form getting submitted... whether or not you are sure.&lt;br /&gt;&lt;br /&gt;"Why not just change the &amp;lt;input type=submit&amp;gt; to &amp;lt;input type=button onclick=formValidate(this)&amp;gt;?"&lt;br /&gt;&lt;br /&gt;Indeed. The driving idea behind caFormTools is that the validation and verification functions would attach themselves to an existing standard HTML form. I didn't want to require page authors to do much other than add appropriate attributes to input elements, and optionally create a div area for content that can be duplicated. Needing to cancel the submit event to show the verification modal isn't a bad thing - the "Yes, I'm sure" link can take care of the rest with a little ingenuity:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;/* Holds "this" variable when form onsubmit fires, which is overridden to return&lt;br /&gt;   false so a verification dialog can be displayed. After verification step, onsubmit&lt;br /&gt;   is reset to null, and the form is submitted */&lt;br /&gt;var frmToSubmit;&lt;br /&gt;&lt;br /&gt;function finalSubmit() {&lt;br /&gt;  frmToSubmit.onsubmit = null; // Stop validation from starting over again.&lt;br /&gt;  frmToSubmit.submit();        // ...and submit the form&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;function formValidate(frm) {&lt;br /&gt;  frmToSubmit = frm;&lt;br /&gt;&lt;br /&gt;/* ... validation steps omitted ... */&lt;br /&gt;&lt;br /&gt;  if (formErrors == "") { openPromptBox(); }&lt;br /&gt;  else { alert ("Please correct the following fields\n" + formErrors); }&lt;br /&gt;  return false;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;// Override onsubmit handler on all forms&lt;br /&gt;for (var f = 0; f &amp;lt; document.forms.length; f++) {&lt;br /&gt;  document.forms[f].onsubmit =  function onsubmit() { return formValidate(this) };&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;With the introduction of the global variable "frmToSubmit" to hold the DOM reference to the form that just got validated, we need only add a link to the the verification dialog which invokes the finalSubmit function. But first we need a framework to create the verification dialog itself, a method of setting its HTML content, and a method of displaying and hiding it.&lt;br /&gt;&lt;br /&gt;The dialog consists of two div blocks, one that is empty, obscuring the entire page and preventing interaction with anything on it; and one containing the prompt itself, centered, taking up half the page, with a z-index one higher. The framework is a closure that holds references to the div blocks, and setters to set the HTML content of the prompt, and making it visible or hidden.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;var highestZ = 0; // Set this higher if page has positioned attributes with z-indices&lt;br /&gt;&lt;br /&gt;function promptBoxClosures() {&lt;br /&gt;  var fullScreenBox = document.createElement('div');&lt;br /&gt;  var fs = fullScreenBox.style;&lt;br /&gt;  fs.zIndex     = highestZ;&lt;br /&gt;  fs.position   = "absolute";&lt;br /&gt;  fs.top        = "0px";&lt;br /&gt;  fs.left       = "0px";&lt;br /&gt;  fs.width      = "100%";&lt;br /&gt;  fs.height     = "100%";&lt;br /&gt;  fs.opacity    = "0.7";&lt;br /&gt;  fs.filter     = "alpha(opacity=70)";&lt;br /&gt;  fs.background = "black";&lt;br /&gt;  fs.border     = "0px";&lt;br /&gt;  fs.display    = "none";&lt;br /&gt;  document.body.appendChild(fullScreenBox);&lt;br /&gt;&lt;br /&gt;  var promptBox = document.createElement('div');&lt;br /&gt;  var ps = promptBox.style;&lt;br /&gt;  ps.display    = "none"&lt;br /&gt;  ps.position   = "absolute"&lt;br /&gt;  ps.top        = "25%"&lt;br /&gt;  ps.left       = "25%"&lt;br /&gt;  ps.width      = "50%"&lt;br /&gt;  ps.height     = "50%"&lt;br /&gt;  ps.border     = "3px solid #A4A4A4"&lt;br /&gt;  ps.background = "#E4E4E4"&lt;br /&gt;  ps.padding    = "10px"&lt;br /&gt;  ps.zIndex     = highestZ + 1;&lt;br /&gt;  document.body.appendChild(promptBox);&lt;br /&gt;&lt;br /&gt;  openPromptBox = function() {&lt;br /&gt;    fs.display = 'inline';&lt;br /&gt;    ps.display = 'inline';&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  closePromptBox = function() {&lt;br /&gt;    fs.display = 'none';&lt;br /&gt;    ps.display = 'none';&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  setPrompt = function(html) {&lt;br /&gt;    promptBox.innerHTML = html;&lt;br /&gt;  }&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;The last step to bring everything together is to have the validation function iterate through all the form elements and build the prompt box's HTML to show the non-blank elements and their values, and to call openPromptBox:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;function formValidate(frm) {&lt;br /&gt;  frmToSubmit = frm;&lt;br /&gt;  var formErrors = "";&lt;br /&gt;  elemsToDisable = new Array();&lt;br /&gt;  var promptHeader = "Is this information correct?&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&amp;lt;table&amp;gt;";&lt;br /&gt;  var promptTrailer = "&amp;lt;/table&amp;gt;&amp;lt;br&amp;gt;&amp;lt;a href=# onclick=finalSubmit()&amp;gt;Yes, continue&amp;lt;/a&amp;gt;"&lt;br /&gt;      + "&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;"&lt;br /&gt;      + "&amp;lt;a href=# onclick=closePromptBox()&amp;gt;No, let me make changes&amp;lt;/a&amp;gt;";&lt;br /&gt;  var promptItems = "";&lt;br /&gt;&lt;br /&gt;  for (var e = 0; e &amp;lt; frm.elements.length; e++) {&lt;br /&gt;    var el = frm.elements[e];&lt;br /&gt;    if (textInput.test(el.type) || /select/.test(el.type)) {&lt;br /&gt;      var msg = validateElem(el);&lt;br /&gt;      if (msg == "" &amp;amp;&amp;amp; el.value == "") { elemsToDisable.push(el); }&lt;br /&gt;      formErrors += msg;&lt;br /&gt;    }&lt;br /&gt;    if (formErrors == "" &amp;amp;&amp;amp; el.value != "" &amp;amp;&amp;amp; /hidden|submit/.test(el.type) == false) {&lt;br /&gt;      promptItems += getPromptItem(el);&lt;br /&gt;    }&lt;br /&gt;  }&lt;br /&gt;  if (formErrors == "") {&lt;br /&gt;    setPrompt(promptHeader + promptItems + promptTrailer);&lt;br /&gt;    openPromptBox();&lt;br /&gt;    return false;&lt;br /&gt;  }&lt;br /&gt;  alert ("Please correct the following fields\n" + formErrors);&lt;br /&gt;  return false;&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;a href="http://curtisquarterly.blogspot.com/2010/06/demo-page-for-caformtools.html"&gt;Test it out&lt;/a&gt; or &lt;a href="http://sites.google.com/site/ceauterytest/Home/caFormTools.js"&gt;Download the source code&lt;/a&gt;!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11885757-8487627563468363037?l=cautery.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://cautery.blogspot.com/feeds/8487627563468363037/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://cautery.blogspot.com/2010/09/form-validation-using-custom-attributes.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11885757/posts/default/8487627563468363037'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11885757/posts/default/8487627563468363037'/><link rel='alternate' type='text/html' href='http://cautery.blogspot.com/2010/09/form-validation-using-custom-attributes.html' title='Form validation using custom attributes'/><author><name>Curtis Autery</name><uri>https://profiles.google.com/107677530285177731535</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh4.googleusercontent.com/--J3C_khtLqQ/AAAAAAAAAAI/AAAAAAAADT4/e2Qa5ncfAMk/s512-c/photo.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh4.ggpht.com/_YH1HTjXGzNU/TAKLTkWPXaI/AAAAAAAAC_I/YFeUGpNFitI/s72-c/01-intro.PNG.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11885757.post-6723515774778817279</id><published>2010-09-27T18:49:00.002-04:00</published><updated>2010-09-28T09:31:15.558-04:00</updated><title type='text'>Building a Java Applet Sudoku Bot</title><content type='html'>(Warning: huge and crazy, with no practical target audience that I can imagine.)&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;&lt;b&gt;Background&lt;/b&gt;&lt;/blockquote&gt;&lt;br /&gt;I've been interested in the game of Sudoku since it became popular a few years ago, and have often daydreamed about what approach one could take to programmatically solve a puzzle. Although I'm not an expert player, when I play I find that I see complex hints about what numbers *must* go here, or *can not* go there. Explaining to another person the logic of each choice would be fairly simple, but coding a straightforward approach for a computer to do the same thing had, until recently, eluded me. I have now successfully licked that problem, and have written a moderately functional solver applet in Java.&lt;br /&gt;&lt;br /&gt;This entry takes you through the start-to-finish process of the applet's creation. I decided from the outset not to make this a Swing applet, and to manage event handling and painting directly.&lt;br /&gt;&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;Let's start with the basics of applets, handling screen painting, drawing a board, and creating a framework to draw numbers on individual squares.&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;&lt;b&gt;Applets&lt;/b&gt;&lt;/blockquote&gt;&lt;br /&gt;This will not function as a tutorial on the Java programming language, or best practices involving applets, but here is a brief list of things that need to be done to get an applet off the ground quickly:&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Import the Abstract Window Toolkit classes (java.awt.*) &lt;/li&gt;&lt;li&gt;Extend java.applet.Applet or its subclass javax.swing.JApplet &lt;/li&gt;&lt;li&gt;Override the init(), paint(), and update() methods &lt;/li&gt;&lt;li&gt;Compile &lt;/li&gt;&lt;li&gt;Create an HTML file to load the applet &lt;/li&gt;&lt;/ul&gt;Consider the following code:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;import java.awt.*;&lt;br /&gt;public class HelloWorldApplet extends java.applet.Applet {&lt;br /&gt;  public void init() {  }&lt;br /&gt;  public void paint(Graphics g){ update(g); }&lt;br /&gt;  public void update(Graphics g) { g.drawString("Hello World", 20, 20); }&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;The first line imports the AWT package, which lets you draw on a canvas. There are individual classes that handle graphics and image objects, fonts, colors, drawing rectangles and circles, arbitrary shapes, loading and parsing jpeg files, etc. It's easier (lazier) to just grab them all with one import statement.&lt;br /&gt;&lt;br /&gt;The class declaration says that I am subclassing java.applet.Applet, the class that presents a canvas to a web browser, and handles client security to prevent you from writing to disk, taking over a user's machine, what have you (see Sun's &lt;a href="http://java.sun.com/docs/books/tutorial/deployment/applet/security.html"&gt;applet security&lt;/a&gt; page).&lt;br /&gt;&lt;br /&gt;When an applet is loaded, its init() method is called, followed by the start() method. If the applet is restarted without being unloaded, start() gets called again, but init() does not. This won't be much of a consideration until we get into finishing one game and starting the next, so for now we'll just override init() and leave it at that.&lt;br /&gt;&lt;br /&gt;The paint() and update() overrides are a standard trick to reduce flicker. The java.applet.Applet version of update() first clears the canvas, then repaints it, which is the main reason otherwise nice applets flicker.&lt;br /&gt;&lt;br /&gt;The standard idiom I use for non-Swing applets is to have paint() just call update(), and have update() do one thing: draw a predefined image (or in this case, a fixed string). All the graphics work will be done outside of the overridden method, followed by a single call to repaint(). This is a technique normally referred to as "double-buffering".&lt;br /&gt;&lt;br /&gt;The above code should compile under any modern javac, should you want to test it out. Once compiled, we just need an HTML file with an applet tag to load the class file:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&amp;lt;!doctype html&amp;gt;&amp;lt;html&amp;gt;&amp;lt;body&amp;gt;&lt;br /&gt;  &amp;lt;applet width="100" height="50" code="HelloWorldApplet.class"&amp;gt;&amp;lt;/applet&amp;gt;&lt;br /&gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&lt;/pre&gt;&lt;br /&gt;...and opening that file in a browser will reveal our coveted "Hello World" text that we were so anxious to see. OK, maybe nobody was anxious about it. Anyway, here it is:&lt;br /&gt;&lt;br /&gt;&lt;img src="http://lh5.ggpht.com/_YH1HTjXGzNU/TBkrGHuPIOI/AAAAAAAADAw/2ycQOfBO_5w/s800/HWApplet.PNG" /&gt;&lt;br /&gt;&lt;br /&gt;Nothing fancy, as expected. What we get from this, however, is a framework we can extend to actually do something interesting.&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;&lt;b&gt;Drawing the board&lt;/b&gt;&lt;/blockquote&gt;&lt;br /&gt;To draw the board, the g.drawString above will need to be changed to g.drawImage, and code to declare an Image object and draw to it during initialization will need to be added.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;import java.awt.*;&lt;br /&gt;&lt;br /&gt;public class DrawBoard extends java.applet.Applet {&lt;br /&gt;&lt;br /&gt;  Image board;  // Image object representing the entire board&lt;br /&gt;&lt;br /&gt;  public void init() {&lt;br /&gt;    board = createImage(451, 451);&lt;br /&gt;    Graphics bg = board.getGraphics();&lt;br /&gt;&lt;br /&gt;    // Step 1 - Draw a grid of light-gray boxes, omitting every third line&lt;br /&gt;    bg.setColor(Color.LIGHT_GRAY);&lt;br /&gt;    for (int i = 0; i &amp;lt; 10; i++) {&lt;br /&gt;      if (i % 3 &amp;gt; 0) {&lt;br /&gt;        bg.drawLine(i * 50, 0, i * 50, 450);&lt;br /&gt;        bg.drawLine(0, i * 50, 450, i * 50);&lt;br /&gt;      }&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    // Step 2 - Use larger black boxes to divide the above grid into 3x3 sections&lt;br /&gt;    bg.setColor(Color.BLACK);&lt;br /&gt;    for (int i = 0; i &amp;lt; 4; i++) {&lt;br /&gt;      bg.drawLine(i * 150, 0, i * 150, 450);&lt;br /&gt;      bg.drawLine(0, i * 150, 450, i * 150);&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  public void paint(Graphics g){ update(g); }&lt;br /&gt;  public void update(Graphics g) { g.drawImage(board, 0, 0, this); }&lt;br /&gt;&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;Not very different than what we started with. The main additions are in the init() method, where small grey squares and large black squares are drawn via criss-crossing lines spanning the width and height of the canvas. The drawLine() method operates on a graphics object, which allows painting lines, rectangles, circles, and characters onto a canvas or an image object. The drawLine() method takes four arguments, the x and y coordinates of the start and stop point.&lt;br /&gt;&lt;br /&gt;In Step 1, I'm drawing 6 horizontal and vertical lines with gaps after each pair. In Step 2, a large Noughts and Crosses board is drawn filling in the gaps.&lt;br /&gt;&lt;br /&gt;Two things worth noting there are that drawLine is painting onto the board Image object, not onto the applet's canvas, and that repaint() isn't called since init() occurs before the canvas gets painted. When init() finishes, an initial paint() call will occur automatically. After a game is running, drawing numbers onto the grid will require calling repaint() to get them to appear on the canvas.&lt;br /&gt;&lt;br /&gt;The HTML file should also be updated to set the applet's size, and to help the user visually discern where it's boundary is:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&amp;lt;!doctype html&amp;gt;&lt;br /&gt;&amp;lt;html&amp;gt;&lt;br /&gt;  &amp;lt;head&amp;gt;&lt;br /&gt;    &amp;lt;style&amp;gt;body {background: #ccc; text-align: center;}&amp;lt;/style&amp;gt;&lt;br /&gt;    &amp;lt;title&amp;gt;Sudoku Bot&amp;lt;/title&amp;gt;&lt;br /&gt;  &amp;lt;/head&amp;gt;&lt;br /&gt;  &amp;lt;body&amp;gt;&lt;br /&gt;    &amp;lt;applet width="451" height="451" code="DrawBoard.class"&amp;gt;&amp;lt;/applet&amp;gt;&lt;br /&gt;  &amp;lt;/body&amp;gt;&lt;br /&gt;&amp;lt;/html&amp;gt;&lt;/pre&gt;After compiling and loading the above file, here is the result:&lt;br /&gt;&lt;br /&gt;&lt;img src="http://lh5.ggpht.com/_YH1HTjXGzNU/TBkrGQUqTaI/AAAAAAAADA0/AfQcdbNrp-4/s800/DrawBoard.PNG" /&gt;&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;&lt;b&gt;Drawing numbers&lt;/b&gt;&lt;/blockquote&gt;&lt;br /&gt;In the above board, the square edges are 50 pixels apart, meaning there are 49 unused pixels between then that can be used to draw numbers or highlight squares with a different background color. The function below can be used as a generic number generator. Pass it a square position (zero indexed), the number to draw, and what color it should be, and it will create a 49x49 pixel image, draw a 25 point number in the middle of it, and attach it to the main board image on the correct square.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;public void drawNumber(int x, int y, int num, Color c) {&lt;br /&gt;  Image square = createImage(49, 49);&lt;br /&gt;  Graphics sg = square.getGraphics();&lt;br /&gt;  sg.setColor(c);&lt;br /&gt;  sg.setFont(new Font(Font.SANS_SERIF, Font.PLAIN, 25));&lt;br /&gt;  sg.drawString(Integer.toString(num), 18, 34);&lt;br /&gt;&lt;br /&gt;  Graphics bg = board.getGraphics();&lt;br /&gt;  bg.drawImage(square, x * 50 + 1, y * 50 + 1, this);&lt;br /&gt;  repaint();&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;Although workable, there are a few problems with leaving this method as-is. First, we're always going to be using the same font, so it shouldn't be generated each time the method is called. Second, the font isn't anti-aliased, so it looks like this:&lt;br /&gt;&lt;br /&gt;&lt;img src="http://lh5.ggpht.com/_YH1HTjXGzNU/TBuCBlB_RaI/AAAAAAAADBI/WkUjkYW59vM/s800/Anti-alias_Off.PNG" /&gt;&lt;br /&gt;&lt;br /&gt;The last major issue I see is that there is no error checking to make sure the referenced square exists, or the number to draw is between 1 and 9. Arguably, all 9 numbers should be generated as image objects when the applet initializes, and used as needed, and I may add that functionality later. At this point, I'm not concerned with the overhead of making java draw a digit "from scratch" vs. using predefined tiles. I'm also not going to address error checking just yet, but it's on the to-do list. For now, I've changed the method to use a predefined font, and have turned on anti-aliasing, making the numbers look a little better:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;Font f = new Font(Font.SANS_SERIF, Font.PLAIN, 25); // Font for drawing the numbers&lt;br /&gt;public void drawNumber(int x, int y, int num, Color c) {&lt;br /&gt;  // Create square image object, populate it with a number roughly in the middle&lt;br /&gt;  Image square = createImage(49, 49);&lt;br /&gt;  Graphics2D sg = (Graphics2D)square.getGraphics();&lt;br /&gt;  sg.setColor(c);&lt;br /&gt;  sg.setFont(f);&lt;br /&gt;  sg.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);&lt;br /&gt;  sg.drawString(Integer.toString(num), 18, 34);&lt;br /&gt;&lt;br /&gt;  // Add this image object to the board, mindful of the gridlines&lt;br /&gt;  Graphics bg = board.getGraphics();&lt;br /&gt;  bg.drawImage(square, x * 50 + 1, y * 50 + 1, this);&lt;br /&gt;  repaint();&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;Graphics2D subclasses the Graphics class, hence I can leave the drawNumber method unaltered other than defining the "sg" object, and the addition of the setRenderingHint method. Using this to turn on anti-aliasing gives us a better look to the numbers drawn with a large font:&lt;br /&gt;&lt;br /&gt;&lt;img src="http://lh4.ggpht.com/_YH1HTjXGzNU/TBuCBzA1ZSI/AAAAAAAADBM/U4Xx3GCvI4k/s800/Anti-alias_On.PNG" /&gt;&lt;br /&gt;&lt;br /&gt;The last additions to the code for this entry will be to pull the board drawing steps into their own method, and add a "populate" method to throw a few sample numbers into squares to prove that drawNumber() does what it should. Init can then be reduced to descriptive steps:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;// Initialize the applet by drawing a Sudoku board, and populating a 3x3 section&lt;br /&gt;@Override&lt;br /&gt;public void init() {&lt;br /&gt;  drawBoard();&lt;br /&gt;  populate();&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;The @Override annotation has also been added, which doesn't affect the way the applet runs at all, but is more of a compile-time hint which, among other things, throws an error if the method doesn't actually override anything.&lt;br /&gt;&lt;br /&gt;The populate() method is very straightforward, just iterating through 9 numbers and placing them in the upper-left section of the board:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;public void populate() {&lt;br /&gt;  int count = 0;&lt;br /&gt;  for (int h = 0; h &amp;lt; 3; h++) {&lt;br /&gt;    for (int w = 0; w &amp;lt; 3; w++) {&lt;br /&gt;      drawNumber(w, h, ++count, Color.red);&lt;br /&gt;    }&lt;br /&gt;  }&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;This makes the applet code thusfar look like this:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;import java.awt.*;&lt;br /&gt;&lt;br /&gt;public class Sudoku extends java.applet.Applet {&lt;br /&gt;&lt;br /&gt;  // Initialize the applet by drawing a Sudoku board, and populating a 3x3 section&lt;br /&gt;  @Override&lt;br /&gt;  public void init() {&lt;br /&gt;    drawBoard();&lt;br /&gt;    populate();&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  Image board;  // Image object representing the entire board&lt;br /&gt;  Font f = new Font(Font.SANS_SERIF, Font.PLAIN, 25); // Font for drawing the numbers&lt;br /&gt;&lt;br /&gt;  /* Standard paint overrides to cut down on flicker. Note that there will be no&lt;br /&gt;     controls painted in the applet, just the board image; hence only one thing for&lt;br /&gt;     paint and update to do. */&lt;br /&gt;  @Override&lt;br /&gt;  public void paint(Graphics g){ update(g); }&lt;br /&gt;&lt;br /&gt;  @Override&lt;br /&gt;  public void update(Graphics g) { g.drawImage(board, 0, 0, this); }&lt;br /&gt;&lt;br /&gt;  public void drawBoard() {&lt;br /&gt;    board = createImage(451, 451);&lt;br /&gt;    Graphics bg = board.getGraphics();&lt;br /&gt;&lt;br /&gt;    // Step 1 - Draw a 9x9 grid of light-gray boxes, omitting every third line&lt;br /&gt;    bg.setColor(Color.LIGHT_GRAY);&lt;br /&gt;    for (int i = 0; i &amp;lt; 10; i++) {&lt;br /&gt;      if (i % 3 &amp;gt; 0) {&lt;br /&gt;        bg.drawLine(i * 50, 0, i * 50, 450);&lt;br /&gt;        bg.drawLine(0, i * 50, 450, i * 50);&lt;br /&gt;      }&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    // Step 2 - Use larger black boxes to divide the above grid into 3x3 sections&lt;br /&gt;    bg.setColor(Color.BLACK);&lt;br /&gt;    for (int i = 0; i &amp;lt; 4; i++) {&lt;br /&gt;      bg.drawLine(i * 150, 0, i * 150, 450);&lt;br /&gt;      bg.drawLine(0, i * 150, 450, i * 150);&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;  // Draw number "num" using color c at position x,y, where x and y are between 0 and 8&lt;br /&gt;  public void drawNumber(int x, int y, int num, Color c) {&lt;br /&gt;    // Create square image object, populate it with a number roughly in the middle&lt;br /&gt;    Image square = createImage(49, 49);&lt;br /&gt;    Graphics2D sg = (Graphics2D)square.getGraphics();&lt;br /&gt;    sg.setColor(c);&lt;br /&gt;    sg.setFont(f);&lt;br /&gt;    sg.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);&lt;br /&gt;    sg.drawString(Integer.toString(num), 18, 34);&lt;br /&gt;&lt;br /&gt;    // Add this image object to the board, mindful of the gridlines&lt;br /&gt;    Graphics bg = board.getGraphics();&lt;br /&gt;    bg.drawImage(square, x * 50 + 1, y * 50 + 1, this);&lt;br /&gt;    repaint();&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  public void populate() {&lt;br /&gt;    int count = 0;&lt;br /&gt;    for (int h = 0; h &amp;lt; 3; h++) {&lt;br /&gt;      for (int w = 0; w &amp;lt; 3; w++) {&lt;br /&gt;        drawNumber(w, h, ++count, Color.red);&lt;br /&gt;      }&lt;br /&gt;    }&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;}&lt;/pre&gt;... and do this:&lt;br /&gt;&lt;br /&gt;&lt;img src="http://lh5.ggpht.com/_YH1HTjXGzNU/TBkrHAjTtNI/AAAAAAAADA4/9DEYNFfnD5c/s800/Populate.PNG" /&gt;&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;&lt;b&gt;What's in that cell?&lt;/b&gt;&lt;/blockquote&gt;&lt;br /&gt;We need a mechanism to track what number is in each cell, whether it is "owned" by the user or the bot, and a visual cue of whether the square is selected and accepting input. A straightforward way to accomplish this is to use a new class to turn Sudoku cells into objects whose properties can be bundled together. This prevents us from needing several arrays of colors, integers, points, and booleans to manage all the cell properties. Being able to arbitrarily create objects like this is one of the main selling points of Java in the first place, so we should leverage that wherever it is appropriate.&lt;br /&gt;&lt;br /&gt;Here is the first draft of the SudokuCell class, which will be compiled separately and included in the jar file that our applet will be running from:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;package cea.demos;&lt;br /&gt;import java.awt.Color;&lt;br /&gt;import java.awt.Point;&lt;br /&gt;&lt;br /&gt;public class SudokuCell {&lt;br /&gt;  Point square;&lt;br /&gt;  Color background;&lt;br /&gt;  Color fontColor;&lt;br /&gt;  int number;&lt;br /&gt;  Boolean userAvailable;&lt;br /&gt;&lt;br /&gt;  public SudokuCell(Point s, Color b, Color f, int n, Boolean a) {&lt;br /&gt;    this.square = s;&lt;br /&gt;    this.background = b;&lt;br /&gt;    this.fontColor = f;&lt;br /&gt;    this.number = n;&lt;br /&gt;    this.userAvailable = a;&lt;br /&gt;  }&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;So far, everything is simple. There is one constructor with public attributes that can be directly manipulated. As the applet development progresses, we'll see reasons why this is a bad idea, and we'll lock down the properties better and add some setters and getters (public methods that interact with the class's variables, rather than exposing the variables themselves). But for the sake of simplicity and speed, we'll start with everything being directly accessible.&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;&lt;b&gt;Drawing the Cell&lt;/b&gt;&lt;/blockquote&gt;&lt;br /&gt;Back in our main Sudoku class, the drawNumber() method can leverage the new class to simplify its input, and can be expanded to draw the cell's background color in addition to any number that may be in it. Compare the before and after methods:&lt;br /&gt;&lt;br /&gt;Before&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;public void drawNumber(int x, int y, int num, Color c) {&lt;br /&gt;  // Create square image object, populate it with a number roughly in the middle&lt;br /&gt;  Image square = createImage(49, 49);&lt;br /&gt;  Graphics2D sg = (Graphics2D)square.getGraphics();&lt;br /&gt;  sg.setColor(c);&lt;br /&gt;  sg.setFont(f);&lt;br /&gt;  sg.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);&lt;br /&gt;  sg.drawString(Integer.toString(num), 18, 34);&lt;br /&gt;&lt;br /&gt;  // Add this image object to the board, mindful of the gridlines&lt;br /&gt;  Graphics bg = board.getGraphics();&lt;br /&gt;  bg.drawImage(square, x * 50 + 1, y * 50 + 1, this);&lt;br /&gt;  repaint();&lt;br /&gt;}&lt;/pre&gt;After&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;public void drawCell(SudokuCell c) {&lt;br /&gt;  Image cellImage = createImage(49, 49);&lt;br /&gt;  Graphics2D sg = (Graphics2D)cellImage.getGraphics();&lt;br /&gt;&lt;br /&gt;  // Draw background&lt;br /&gt;  sg.setColor(c.background);&lt;br /&gt;  sg.fillRect(0, 0, 49, 49);&lt;br /&gt;&lt;br /&gt;  //Draw number&lt;br /&gt;  if (c.number &amp;gt; 0) {&lt;br /&gt;    sg.setColor(c.fontColor);&lt;br /&gt;    sg.setFont(f);&lt;br /&gt;    sg.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);&lt;br /&gt;    sg.drawString(Integer.toString(c.number), 18, 34);&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  Graphics bg = board.getGraphics();&lt;br /&gt;  bg.drawImage(cellImage, c.square.x * 50 + 1, c.square.y * 50 + 1, this);&lt;br /&gt;  repaint();&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;Other than the method's name change, the first thing that should jump out is that the inputs have changed to simply "SudokuCell c". This way, we don't have to be concerned with making sure all the calls to the method have the variables in the correct order. The cell object has everything.&lt;br /&gt;&lt;br /&gt;Next, before the number is drawn, the background is filled in by setting the paint color to the cell's background color, then a call to fillRect() to paint the entire cell. Other methods can determine the logic of what the color should be prior to calling drawCell. Lastly, a quick check is done to see if the number is greater than 0 before drawing it. This way, all the cells on the board can be initialized to 0 so that they have a non-null value, and the actual 0 digit won't be displayed.&lt;br /&gt;&lt;br /&gt;Now that a mechanism exists to take a cell object and draw it, the board's cell objects can be put into a handy 9x9 array, matching up to their x and y positions on the screen:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;SudokuCell cell[][] = new SudokuCell[9][9];&lt;br /&gt;public void initCells() {&lt;br /&gt;  // Create 81 cell objects&lt;br /&gt;  for (int h = 0; h &amp;lt; 9; h++) {&lt;br /&gt;    for (int w = 0; w &amp;lt; 9; w++) {&lt;br /&gt;      cell[w][h] = new SudokuCell(new Point(w,h),Color.WHITE, Color.RED, 0, true);&lt;br /&gt;    }&lt;br /&gt;  }&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;blockquote&gt;&lt;b&gt;Event handlers&lt;/b&gt;&lt;/blockquote&gt;&lt;br /&gt;In order for the user to be able to set up a board for the bot to solve (or, alternately, solve a board that the bot has set up), the Sudoku applet needs to be able to read mouse and keyboard events. To accomplish this, the java.awt.event.* classes need to be imported, and then three interface classes need to be implemented: MouseMotionListener, which handles move events; MouseListener, which handles click events, and KeyListener, which handles all things keyboard. For each interface, the equivalent event listener needs to be added, and the interface methods must be defined. For example:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;import java.awt.*;&lt;br /&gt;import java.awt.event.*; // Import the event classes&lt;br /&gt;public class Test extends java.applet.Applet&lt;br /&gt;     implements MouseMotionListener { // Declare the interface to be implemented&lt;br /&gt;&lt;br /&gt;  // Add the listener to this class&lt;br /&gt;  public void init() {&lt;br /&gt;    addMouseMotionListener(this);&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  // And finally, define the appropriate methods, and handle them as needed&lt;br /&gt;  public void mouseMoved (MouseEvent e) {&lt;br /&gt;    // Do stuff&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  public void mouseDragged (MouseEvent e) {&lt;br /&gt;    // Do stuff&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;}&lt;/pre&gt;An interface is simply a list of method definitions. "Implementing" one means defining each method declared in the interface definition. Why you would want to do this may not be intuitive; the basic answer is that something you have no control over is going to be calling everything defined in the interface.&lt;br /&gt;&lt;br /&gt;In the case of mouse events, a class imports java.awt.Component and calls the addMouseListener method in order to trap mouse events. After that, the class receives calls to methods mouseClicked(), mouseEntered(), mouseExited(), mousePressed(), and mouseReleased() whenever the user performs mouse actions over the component. The class doesn't need to do anything with all the events, but they all need to be defined in order for the listener to not throw an error. The interface helps you avoid that by throwing compile-time errors if all the methods aren't properly declared.&lt;br /&gt;&lt;br /&gt;What I would like to do with mouse and keyboard events is manage two modes of input: choosing a cell, and entering a number into a cell. While choosing a cell, I would like the background color of the cell you are hovering on change to yellow, then back to white as your mouse moves to a new cell. When a cell is clicked, the second mode kicks in and the hovering action stops until a number on the keyboard is pressed.&lt;br /&gt;&lt;br /&gt;To handle hovering over cells and changing background colors, the mouseMoved() event will suffice. To handle changing modes when a cell is clicked, mouseClicked() will be used. Lastly, receiving the number from the keyboard will be done via the keyTyped() method. This leaves us with seven methods defined in the various interfaces that we don't care about, but that still need to be defined.&lt;br /&gt;&lt;br /&gt;Here is the before and after initial first chunk of the Sudoku class&lt;br /&gt;&lt;br /&gt;Before&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;import java.awt.*;&lt;br /&gt;&lt;br /&gt;public class Sudoku extends java.applet.Applet {&lt;br /&gt;&lt;br /&gt;  // Initialize the applet by drawing a Sudoku board, and populating a 3x3 section&lt;br /&gt;  @Override&lt;br /&gt;  public void init() {&lt;br /&gt;    drawBoard();&lt;br /&gt;    populate();&lt;br /&gt;  }&lt;/pre&gt;After&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;import java.awt.*;&lt;br /&gt;import java.awt.event.*;&lt;br /&gt;&lt;br /&gt;public class Sudoku extends java.applet.Applet&lt;br /&gt; implements MouseMotionListener, MouseListener, KeyListener {&lt;br /&gt;&lt;br /&gt;  @Override&lt;br /&gt;  public void init() {&lt;br /&gt;    drawBoard();&lt;br /&gt;    initCells(); // (More on this later)&lt;br /&gt;    addMouseMotionListener(this);&lt;br /&gt;    addMouseListener(this);&lt;br /&gt;    addKeyListener(this);&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  // Used event handlers:&lt;br /&gt;  public void mouseMoved ( MouseEvent e) {&lt;br /&gt;    // Do stuff&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  public void mouseClicked(MouseEvent e) {&lt;br /&gt;    // Do stuff&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  public void keyTyped(KeyEvent e) {&lt;br /&gt;    // Do stuff&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  // Unused, but required to implement MouseMotionListener&lt;br /&gt;  public void mouseDragged (MouseEvent e) { }&lt;br /&gt;&lt;br /&gt;  //Unused, but required to implement MouseListener&lt;br /&gt;  public void mouseExited  (MouseEvent e) { }&lt;br /&gt;  public void mouseEntered (MouseEvent e) { }&lt;br /&gt;  public void mouseReleased(MouseEvent e) { }&lt;br /&gt;  public void mousePressed (MouseEvent e) { }&lt;br /&gt;&lt;br /&gt;  //Unused, but required to implement KeyListener&lt;br /&gt;  public void keyPressed     (KeyEvent e) { }&lt;br /&gt;  public void keyReleased    (KeyEvent e) { }&lt;/pre&gt;&lt;br /&gt;Some constants would be useful at this point to define the two input modes, as well as a variable to hold the current mode. Since more than two modes may be used as applet development progresses, integers will be used to declare modes instead of a simple boolean.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;static final int CELLBROWSE = 0;&lt;br /&gt;static final int CELLGETKEY = 1;&lt;br /&gt;int inputMode = CELLBROWSE;&lt;/pre&gt;&lt;br /&gt;&lt;blockquote&gt;&lt;b&gt;Browse Mode&lt;/b&gt;&lt;/blockquote&gt;&lt;br /&gt;Browse mode does only one thing: swap background colors. Since swapping background colors can be done in other modes for other reasons, it should be its own method, and mouseMoved() can be extended to the following simple check:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;public void mouseMoved (MouseEvent e) {&lt;br /&gt;  // No processing unless we're in cell browse mode&lt;br /&gt;  if (inputMode != CELLBROWSE) return;&lt;br /&gt;  swapCellBackground(e);&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;Note that no concern is being made over what cell the mouse is hovering over at this point; the entire mouse event is passed to the swap method. Since the intention is to both set the cell you move into to yellow as well as the cell you move out of to white, a variable needs to exist to hold a reference to the last cell that was set to yellow. Also, a separate method should exist whose only purpose is to locate the current cell the mouse is over, regardless of whether backgrounds are being flipped.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;SudokuCell lastCell = new SudokuCell&lt;br /&gt;  (new Point(9,9), Color.BLACK, Color.BLACK, 0, false);&lt;br /&gt;&lt;br /&gt;public SudokuCell getCell(MouseEvent e) {&lt;br /&gt;  int x = e.getX()/50; // Cells are 50 pixels wide&lt;br /&gt;  int y = e.getY()/50;&lt;br /&gt;  if (x &amp;gt; 8) x = 8; // In case the mouse is in the applet&lt;br /&gt;  if (y &amp;gt; 8) y = 8; // but outside of the board&lt;br /&gt;  return cell[x][y];&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;public void swapCellBackground(MouseEvent e) {&lt;br /&gt;  //Get current cell, no additional processing if the cell hasn't changed&lt;br /&gt;  SudokuCell thisCell = getCell(e);&lt;br /&gt;  if (thisCell.equals(lastCell)) return;&lt;br /&gt;&lt;br /&gt;  // Set the old cell's background to white, the new to yellow, then redraw&lt;br /&gt;  lastCell.background = Color.WHITE;&lt;br /&gt;  thisCell.background = Color.YELLOW;&lt;br /&gt;  drawCell(lastCell);&lt;br /&gt;  drawCell(thisCell);&lt;br /&gt;  lastCell = thisCell;&lt;br /&gt;}&lt;/pre&gt;&lt;blockquote&gt;&lt;b&gt;GetKey Mode&lt;/b&gt;&lt;/blockquote&gt;&lt;br /&gt;In this early stage, GetKey mode will be very simple. Clicking a cell not owned by the bot changes the applet to GetKey Mode, and selects that cell, leaving its background yellow. If a key between 1 and 9 is pressed while in this mode, that number appears in red in the selected cell, and the applet switches back to browse mode.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;public void mouseClicked(MouseEvent e) {&lt;br /&gt;  swapCellBackground(e);&lt;br /&gt;  if (lastCell.userAvailable) inputMode = CELLGETKEY;&lt;br /&gt;  else inputMode = CELLBROWSE;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;public void keyTyped(KeyEvent e) {&lt;br /&gt;  // No processing unless we're in getkey mode&lt;br /&gt;  if (inputMode != CELLGETKEY) return;&lt;br /&gt;&lt;br /&gt;  // Validate input&lt;br /&gt;  char c = e.getKeyChar();&lt;br /&gt;  if(c &amp;gt;= '1' &amp;amp;&amp;amp; c &amp;lt;= '9') {&lt;br /&gt;    lastCell.number = Integer.parseInt(String.valueOf(c));&lt;br /&gt;    lastCell.fontColor = Color.RED;&lt;br /&gt;    drawCell(lastCell);&lt;br /&gt;    inputMode = CELLBROWSE;&lt;br /&gt;  }&lt;br /&gt;}&lt;/pre&gt;Now we have a functioning applet that accepts input and tracks all the cells. The complete source code for both classes can be found &lt;a href="http://sites.google.com/site/ceauterytest/Home/Sudoku.java?revision=3"&gt;here&lt;/a&gt; and &lt;a href="http://sites.google.com/site/ceauterytest/Home/SudokuCell.java?revision=1"&gt;here&lt;/a&gt;, and the working applet is available &lt;a href="http://curtisquarterly.blogspot.com/2010/06/sudoku-phase-2-accepting-input.html"&gt;here&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;A screenshot of a "game in play" appears below. No checking is done to see if moves made are legal, and no logic exists yet for having the bot attempt to solve the puzzle - both of which we'll conquer next.&lt;br /&gt;&lt;br /&gt;&lt;img src="http://lh3.ggpht.com/_YH1HTjXGzNU/TCI98pkOZ0I/AAAAAAAADBU/ccDwyTpaiEs/s800/777.PNG" /&gt;&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;&lt;b&gt;Legal moves and solving&lt;/b&gt;&lt;/blockquote&gt;&lt;br /&gt;Below are some screenshots showing behavior of the bot after adding move checking, a pair of board solving strategies, drawing a "Solve" button, error prompts, and adding cell selection by arrow keys. I'll go into the code shortly, but first here are screenshots of three Sudoku boards whose initial layout I grabbed from various websites. All three have non-ambiguous solutions. Two of them the bot can solve, and one it can not.&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;&lt;b&gt;Board 1&lt;/b&gt;&lt;/blockquote&gt;&lt;br /&gt;&lt;img src="http://lh5.ggpht.com/_YH1HTjXGzNU/TDXTiZw2-RI/AAAAAAAADCA/WSgmoa9JhkM/s800/1-before.PNG.jpg" /&gt;&lt;br /&gt;&lt;br /&gt;The board shown here was the first board the bot successfully solved, using a simple "8 of 9" algorithm. When an empty cell's row, column, and 3x3 grid have eight unique digits between them, there is only one digit that will not cause a conflict if placed. In this board, luckily, some 9th digits entered ended up being the 8th digit for another empty cell, so there was a cascade effect until all the cells were filled. A visual explanation may help:&lt;br /&gt;&lt;br /&gt;&lt;img src="http://lh3.ggpht.com/_YH1HTjXGzNU/TDXTklfDuaI/AAAAAAAADCY/hWCDj6A5Do0/s800/expl-1.PNG.jpg" /&gt;&lt;br /&gt;&lt;br /&gt;The highlighted cell above can only have one possible value. The column the cell is in already contains 1, 3, 6, 7, 8, and 9 (shown in blue). Additionally it's 3x3 grid contains 2 and 5 (also blue). This leaves 4 as the only possible digit that can be placed in the cell without conflict. That 4, as it happens, becomes the 8th digit for a nearby empty cell:&lt;br /&gt;&lt;br /&gt;&lt;img src="http://lh4.ggpht.com/_YH1HTjXGzNU/TDXTkzPnDGI/AAAAAAAADCc/60XJGEJbvgY/s800/expl-2.PNG.jpg" /&gt;&lt;br /&gt;&lt;br /&gt;In this cell, previously 1 and 4 were unaccounted for in the cells row, column, and grid, but now that 4 has been added to the grid, this leaves 1 as the only remaining digit. Wash, rinse, and repeat until the board is solved:&lt;br /&gt;&lt;br /&gt;&lt;img src="http://lh5.ggpht.com/_YH1HTjXGzNU/TDXTilIpo4I/AAAAAAAADCE/4RoPn_PvjYA/s800/1-after.PNG.jpg" /&gt;&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;&lt;b&gt;Board 2&lt;/b&gt;&lt;/blockquote&gt;&lt;br /&gt;&lt;img src="http://lh5.ggpht.com/_YH1HTjXGzNU/TDXTi_o41GI/AAAAAAAADCI/_wBbHwve5zg/s800/2-before.PNG.jpg" /&gt;&lt;br /&gt;&lt;br /&gt;This board is not solvable using the 8 of 9 solution method, having too few starting cells ("clue" cells) for that to work. After the bot failed to solve this board, I added a second solution method, similar to the technique that human players use intuitively. The method picks a row, column, or grid, and searches for a number that can only be placed in one of the empty cells. To illustrate:&lt;br /&gt;&lt;br /&gt;&lt;img src="http://lh6.ggpht.com/_YH1HTjXGzNU/TDYC7Ibf9fI/AAAAAAAADCk/4zNKk7A-avs/s800/expl-3.PNG.jpg" /&gt;&lt;br /&gt;&lt;br /&gt;The highlighted cell above is the only open cell in the middle grid that a 9 can be placed in. The two open cells on top cannot contain a 9 because of the 9 in the right grid, nor the two cells on the bottom because of the 9 in the left grid.&lt;br /&gt;&lt;br /&gt;Using a combination of the two solving methods, the bot solves this board without any problems:&lt;br /&gt;&lt;br /&gt;&lt;img src="http://lh3.ggpht.com/_YH1HTjXGzNU/TDXTjWYsXzI/AAAAAAAADCM/FgtR8t6S1oY/s800/2-after.PNG.jpg" /&gt;&lt;br /&gt;&lt;br /&gt;There are still boards that have unique solutions that these two techniques alone are not sufficient to solve. Here is one such example:&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;&lt;b&gt;Board 3&lt;/b&gt;&lt;/blockquote&gt;&lt;br /&gt;&lt;img src="http://lh6.ggpht.com/_YH1HTjXGzNU/TDXTjXzHAJI/AAAAAAAADCQ/tkHOSDHG74U/s800/3-before.PNG.jpg" /&gt;&lt;br /&gt;&lt;br /&gt;The bot's current solving methods can only deduce the values of a handful of this board's empty cells:&lt;br /&gt;&lt;br /&gt;&lt;img src="http://lh5.ggpht.com/_YH1HTjXGzNU/TDXTkdpCb8I/AAAAAAAADCU/ls85dEIvYEk/s800/3-after.PNG.jpg" /&gt;&lt;br /&gt;&lt;br /&gt;A third method needs to be used to solve boards like this: guessing and backtracking. For example, in the board above, an 8 can be placed in two possible open cells in the upper-left grid. If the bot could remember the state of the board, and pick one of the two cells, it could see if that path leads to a solution, and if not, reload the board and pick the other path.&lt;br /&gt;&lt;br /&gt;Unfortunately that method will take some additional code refactoring, as I did not design the bot with that in mind. (As a software developer, I'm very familiar with this situation... if I had a nickel for every new requirement that caused me to retrofit a project or redo it from scratch...)&lt;br /&gt;&lt;br /&gt;So those are the fun screenshots. A link to the applet's current version is at the bottom of this page, or you can continue reading how I tackled checking for legal moves and writing solving algorithms.&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;&lt;b&gt;Legal Moves&lt;/b&gt;&lt;/blockquote&gt;&lt;br /&gt;As in earlier versions of the applet, a two-dimensional array of SudokuCell objects is declared and initialized so that the x and y of the array match the x an y position on the board.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;// Grid representing the board itself&lt;br /&gt;SudokuCell board[][] = new SudokuCell[9][9];&lt;br /&gt;&lt;br /&gt;public void initCells() {&lt;br /&gt;  // Create 81 cell objects&lt;br /&gt;  for (int h = 0; h &amp;lt; 9; h++) {&lt;br /&gt;    for (int w = 0; w &amp;lt; 9; w++) {&lt;br /&gt;      board[w][h] = new SudokuCell(new Point(w,h),Color.WHITE, userColor, 0, true);&lt;br /&gt;    }&lt;br /&gt;  }&lt;br /&gt;  currentCell = board[0][0];&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;Newly added to the applet are two-dimensional arrays representing each row, column and grid, and what cells contain each number. These won't be created as new objects, but will instead contain references to the board[][] objects as numbers are put in play.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;/* Grids where x is an entire column, row, or 3x3 grid, and y+1 is a number in play&lt;br /&gt;   on that grid. These will be references to the 9x9 board above, and exist only to&lt;br /&gt;   speed up searches for legal moves and cross-referencing for solution searching. */&lt;br /&gt;SudokuCell xGroup[][] = new SudokuCell[9][9];&lt;br /&gt;SudokuCell yGroup[][] = new SudokuCell[9][9];&lt;br /&gt;SudokuCell gridGroup[][] = new SudokuCell[9][9];&lt;/pre&gt;&lt;br /&gt;The first dimension of these arrays refers to the row, column, or grid number. xGroup[0] refers to the board's first column. The second dimension refers to which number. The cell containing the number 1 in the first column would be xGroup[0][0].&lt;br /&gt;&lt;br /&gt;These objects start out uninitialized, so to check if the the number 1 has been played in the left column, I simply need to check whether or not xGroup[0][0] is null. If it is, 1 hasn't been played. If it's not, the cell object that gets returned will contain the x and y board position of the cell.&lt;br /&gt;&lt;br /&gt;To add a cell, then, I need to determine from it's board coordinates what the row, column, and grid numbers are, and then check the three group references to see if the number has already been played somewhere that would conflict.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;String promptMsg = ""; // Shown when moves are illegal, and after bot tries to solve&lt;br /&gt;&lt;br /&gt;// Check the row, column, and 3x3 grid to see if n is already in play&lt;br /&gt;public boolean checkGroups(SudokuCell c, int index) {&lt;br /&gt;  promptMsg = "";&lt;br /&gt;  int n = index + 1; // group indices are 0 indexed, but refer to 1 through 9&lt;br /&gt;  if (n &amp;lt; 1 || n &amp;gt; 9) {&lt;br /&gt;    promptMsg = "Number must be between 1 and 9";&lt;br /&gt;    return false;&lt;br /&gt;  }&lt;br /&gt;  if (cellExists(xGroup[c.square.x][index])) {&lt;br /&gt;    promptMsg = "Number " + n + " already used in this column";&lt;br /&gt;    return false;&lt;br /&gt;  }&lt;br /&gt;  if (cellExists(yGroup[c.square.y][index])) {&lt;br /&gt;    promptMsg = "Number " + n + " already used in this row";&lt;br /&gt;    return false;&lt;br /&gt;  }&lt;br /&gt;  if (cellExists(gridGroup[c.gridGroup][index])) {&lt;br /&gt;    promptMsg = "Number " + n + " already used in this 3x3 grid";&lt;br /&gt;    return false;&lt;br /&gt;  }&lt;br /&gt;  return true;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;public Boolean cellExists(SudokuCell c) {&lt;br /&gt;  if (c == null) return false;&lt;br /&gt;  // If bot is solving board, dispense with extra handling and just return true&lt;br /&gt;  if (inputMode == SOLVEBOARD) return true;&lt;br /&gt;  // Unselect currentCell, set it's background to white, and redraw it&lt;br /&gt;  currentCell.selected = false;&lt;br /&gt;  currentCell.background = Color.WHITE;&lt;br /&gt;  drawCell(currentCell);&lt;br /&gt;  // Set the cell in conflict to a cyan background&lt;br /&gt;  c.background = Color.CYAN;&lt;br /&gt;  c.selected = false;&lt;br /&gt;  drawCell(c);&lt;br /&gt;  errorCell = c;&lt;br /&gt;  return true;&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;If false is returned from checkGroups(), promptMsg will be shown to the user by the calling method. If true is returned, the move is legal to make. cellExists() checks whether of not the cell reference is null. If it isn't null, the cell in question is highlighted in cyan to show the user why the number can't be played. The effect looks like this:&lt;br /&gt;&lt;br /&gt;&lt;img src="http://lh5.ggpht.com/_YH1HTjXGzNU/TDYC7EyeQpI/AAAAAAAADCo/qikrlykR8lc/s800/alreadyPlayed.PNG.jpg" /&gt;&lt;br /&gt;&lt;br /&gt;Now all that is needed is a means of adding to and subtracting from these groups:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;int cellsInPlay = 0; // How many cells have numbers in them. Game ends at 81&lt;br /&gt;&lt;br /&gt;// Remove a cell's number from the row, column, and 3x3 grid it is on&lt;br /&gt;public void subtractFromGroups(SudokuCell c) {&lt;br /&gt;  if (c == null || c.number == 0) { return; }&lt;br /&gt;  int n = c.number - 1;&lt;br /&gt;  xGroup[c.square.x][n] = null;&lt;br /&gt;  yGroup[c.square.y][n] = null;&lt;br /&gt;  gridGroup[c.gridGroup][n] = null;&lt;br /&gt;  cellsInPlay--;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;// Attempt to add a number to a cell.&lt;br /&gt;public boolean addToGroups(SudokuCell c, int n) {&lt;br /&gt;  // Nothing to do if the number being added is the one already in the cell&lt;br /&gt;  if (c.number == n) return true;&lt;br /&gt;  int index = n-1;&lt;br /&gt;  if (!checkGroups(c, index)) return false;&lt;br /&gt;  // If overwriting an existing number, remove it from groups first&lt;br /&gt;  if(c.number &amp;gt; 0) { subtractFromGroups(c); }&lt;br /&gt;  xGroup[c.square.x][index] = c;&lt;br /&gt;  yGroup[c.square.y][index] = c;&lt;br /&gt;  gridGroup[c.gridGroup][index] = c;&lt;br /&gt;  c.number = n;&lt;br /&gt;  cellsInPlay++;&lt;br /&gt;  return true;&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;And now we have a comprehensive means of checking for legal moves that both the user and bot can use. Best of all, there is no iterating through arrays or collections involved, a modest speedup at the cost of some extra memory.&lt;br /&gt;&lt;br /&gt;It's pretty straightforward how the column and row groups are determined (they just follow the x and y coordinates of the cell, respectively), but some explanation is in order regarding the new object variable "gridGroup". Here is the latest revision of the SudokuCell class, which will help explain:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;package cea.demos;&lt;br /&gt;import java.awt.Color;&lt;br /&gt;import java.awt.Point;&lt;br /&gt;&lt;br /&gt;public class SudokuCell {&lt;br /&gt;  Point square;&lt;br /&gt;  Color background;&lt;br /&gt;  Color fontColor;&lt;br /&gt;  int number;&lt;br /&gt;  Boolean userAvailable;&lt;br /&gt;&lt;br /&gt;  int gridGroup;&lt;br /&gt;  Boolean selected;&lt;br /&gt;&lt;br /&gt;  public SudokuCell(Point s, Color b, Color f, int n, Boolean a) {&lt;br /&gt;    this.square = s;&lt;br /&gt;    this.background = b;&lt;br /&gt;    this.fontColor = f;&lt;br /&gt;    this.number = n;&lt;br /&gt;    this.userAvailable = a;&lt;br /&gt;    this.gridGroup = (s.y/3) * 3 + (s.x/3);&lt;br /&gt;    this.selected = false;&lt;br /&gt;  }&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;The gridGroup variable is calculated when the object is created, using a handy feature of integers: dropping the remainder when you divide. Grids 0, 1, and 2 are the top three grids, 3, 4, and 5 are the middle three, and 6, 7, and 8 are at the bottom. Also new to the class is the "selected" variable, which indicates to the cellDraw method whether or not to draw an underline in the cell image. The Sudoku class assures that only one object is selected, but the object model itself does not - more bad mojo that I may come back and clean up later.&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;&lt;b&gt;Solving a board&lt;/b&gt;&lt;/blockquote&gt;&lt;br /&gt;Here is a method used in the "8 of 9" solving technique. It takes a cell reference, and counts the number of unique digits in play in the three groups around it, keeping track of the last unplayed digit. If there were 8 unique digits already in play, then "number" is the ninth. (Or rather, "number + 1" is, since the groups are zero-indexed.)&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;public int findNinth(SudokuCell c) {&lt;br /&gt;  if (c.number &amp;gt; 0) return 0; // Only operate on empty squares&lt;br /&gt;  int size = 0;&lt;br /&gt;  int number = -1;&lt;br /&gt;  for (int n = 0; n &amp;lt; 9; n++) {&lt;br /&gt;    if (xGroup[c.square.x][n] != null || yGroup[c.square.y][n] != null&lt;br /&gt;            || gridGroup[c.gridGroup][n] != null) {&lt;br /&gt;      size++;&lt;br /&gt;    } else {&lt;br /&gt;      number = n;&lt;br /&gt;    }&lt;br /&gt;  }&lt;br /&gt;  return size == 8 ? number + 1 : 0;&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;Here is the board solving algorithm employing only that technique. It basically iterates through all the empty cells and tries to "findNinth" on them in a loop until either all 81 are filled in, or an iteration completes with no new cell being added.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;public void boardSolve() {&lt;br /&gt;  boolean cellAdded;&lt;br /&gt;  int addNumber;&lt;br /&gt;  SudokuCell c;&lt;br /&gt;  while (cellsInPlay &amp;lt; 81) {&lt;br /&gt;    cellAdded = false;&lt;br /&gt;    for (int h = 0; h &amp;lt; 9; h++) {&lt;br /&gt;      for (int w = 0; w &amp;lt; 9; w++) {&lt;br /&gt;        c = board[w][h];&lt;br /&gt;        addNumber = findNinth(c);&lt;br /&gt;        if(addNumber &amp;gt; 0) {&lt;br /&gt;          c.fontColor = botColor;&lt;br /&gt;          c.userAvailable = false;&lt;br /&gt;          // Break if the "finder" returns a number that can't be added&lt;br /&gt;          if(!addToGroups(c, addNumber)) return;&lt;br /&gt;          drawCell(c);&lt;br /&gt;          cellAdded = true;&lt;br /&gt;        }&lt;br /&gt;      }&lt;br /&gt;    }&lt;br /&gt;    if (!cellAdded) {&lt;br /&gt;      prompt("Could not solve");&lt;br /&gt;      return;&lt;br /&gt;    }&lt;br /&gt;  }&lt;br /&gt;  prompt ("Solved!");&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;The "number can only be placed in one cell" method is more involved. The checkEmpties() method below receives an arbitrary ArrayList of cells, then builds a hash of digits where the values are either the only cell the digit can be placed, or are the "nullCell" object, if a digit can be placed in more than one cell.&lt;br /&gt;&lt;br /&gt;The method then iterates through the hash, and for each digit not pointing to nullCell, the digit is placed in the appropriate cell. For to be thorough, even though the method previously checked each cell to see if the digit could be put there, the method throws an error if the attempt to actually place the digit fails.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;// Refers to an object that can be put in a hash representing a cell not on the board&lt;br /&gt;SudokuCell nullCell = new SudokuCell(new Point(9,9), botColor, botColor, 0, false);&lt;br /&gt;&lt;br /&gt;/* Take a group of cells, check the empty cells to see what can be added to them.&lt;br /&gt;   Look for digits that can only be added to one cell, and add the digit there */&lt;br /&gt;public boolean checkEmpties(ArrayList cells) throws Exception {&lt;br /&gt;    HashMap&amp;lt;Integer, SudokuCell&amp;gt; digits;&lt;br /&gt;    SudokuCell c;&lt;br /&gt;    boolean cellAdded = false;&lt;br /&gt;    int index;&lt;br /&gt;      /* Get hash of key = digit, value = cell it can be placed in.&lt;br /&gt;         If value = null, digit is already in use. If value = nullCell,&lt;br /&gt;         more than one digit can be placed in cell*/&lt;br /&gt;      digits = new HashMap&amp;lt;Integer, SudokuCell&amp;gt;();&lt;br /&gt;      Iterator i = cells.iterator();&lt;br /&gt;      while (i.hasNext()) {&lt;br /&gt;        c = (SudokuCell)i.next();&lt;br /&gt;        for (index = 0; index &amp;lt; 9; index++) {&lt;br /&gt;          if (checkGroups(c, index)) {&lt;br /&gt;            if (digits.containsKey(index)) digits.put(index, nullCell);&lt;br /&gt;            else digits.put(index, c);&lt;br /&gt;          }&lt;br /&gt;        }&lt;br /&gt;      }&lt;br /&gt;&lt;br /&gt;      // Iterate through hash keys, add numbers to appropriate cells&lt;br /&gt;      i = digits.keySet().iterator();&lt;br /&gt;      int n;&lt;br /&gt;      while(i.hasNext()) {&lt;br /&gt;        index = (Integer)i.next();&lt;br /&gt;        n = index + 1;&lt;br /&gt;        c = (SudokuCell)digits.get(index);&lt;br /&gt;        if (!c.equals(nullCell)) {&lt;br /&gt;          c.fontColor = botColor;&lt;br /&gt;          c.userAvailable = false;&lt;br /&gt;          if(!addToGroups(c, n)) {&lt;br /&gt;            c.background = Color.CYAN;&lt;br /&gt;            drawCell(c);&lt;br /&gt;            throw new Exception("Couldn't add " + n + " to cell");&lt;br /&gt;          }&lt;br /&gt;          drawCell(c);&lt;br /&gt;          cellAdded = true;&lt;br /&gt;        }&lt;br /&gt;      }&lt;br /&gt;  return cellAdded;&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;The nice thing about this method is that it doesn't care where on the board these cells are, making it simpler to modify the applet to handle nonstandard board layouts, where "grids" are not uniform 3x3 set of cells.&lt;br /&gt;&lt;br /&gt;Finally, the boardSolve() method needs to be extended to use this method, passing it ArrayLists that encompass each row, column, and grid.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;public void boardSolve() {&lt;br /&gt;  boolean cellAdded;&lt;br /&gt;  int addNumber;&lt;br /&gt;  SudokuCell c;&lt;br /&gt;  while (cellsInPlay &amp;lt; 81) {&lt;br /&gt;    cellAdded = false;&lt;br /&gt;     /* Pass one. For each empty cell, check the total number of unique&lt;br /&gt;       digits in the cell's grid, row, and column. If the total is 8,&lt;br /&gt;       the unused digit belongs in the empty cell. */&lt;br /&gt;    for (int h = 0; h &amp;lt; 9; h++) {&lt;br /&gt;      for (int w = 0; w &amp;lt; 9; w++) {&lt;br /&gt;        c = board[w][h];&lt;br /&gt;        addNumber = findNinth(c);&lt;br /&gt;        if(addNumber &amp;gt; 0) {&lt;br /&gt;          c.fontColor = botColor;&lt;br /&gt;          c.userAvailable = false;&lt;br /&gt;          // Break if the "finder" returns a number that can't be added&lt;br /&gt;          if(!addToGroups(c, addNumber)) return;&lt;br /&gt;          drawCell(c);&lt;br /&gt;          cellAdded = true;&lt;br /&gt;        }&lt;br /&gt;      }&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    /* Pass two. For each column's empty cells, check what digits can legally&lt;br /&gt;       be added to each cell. When a digit can only be added to one cell in&lt;br /&gt;       the column, put it there. */&lt;br /&gt;    if (cellsInPlay &amp;lt; 81) {&lt;br /&gt;      ArrayList&amp;lt;SudokuCell&amp;gt; cells = new ArrayList&amp;lt;SudokuCell&amp;gt;();&lt;br /&gt;      try {&lt;br /&gt;        for (int column = 0; column &amp;lt; 9; column++) {&lt;br /&gt;          cells.clear();&lt;br /&gt;          for (int n = 0; n &amp;lt; 9; n++) {&lt;br /&gt;            c = board[column][n];&lt;br /&gt;            if (c.number == 0) cells.add(c);&lt;br /&gt;          }&lt;br /&gt;          if (checkEmpties(cells)) cellAdded = true;&lt;br /&gt;        }&lt;br /&gt;        // Do the same for rows&lt;br /&gt;        if (cellsInPlay &amp;lt; 81) {&lt;br /&gt;          for (int row = 0; row &amp;lt; 9; row++) {&lt;br /&gt;            cells.clear();&lt;br /&gt;            for (int n = 0; n &amp;lt; 9; n++){&lt;br /&gt;              c = board[n][row];&lt;br /&gt;              if (c.number == 0) cells.add(c);&lt;br /&gt;            }&lt;br /&gt;            if (checkEmpties(cells)) cellAdded = true;&lt;br /&gt;          }&lt;br /&gt;        }&lt;br /&gt;        // And for grids&lt;br /&gt;        if (cellsInPlay &amp;lt; 81) {&lt;br /&gt;          for (int grid = 0; grid &amp;lt; 9; grid++) {&lt;br /&gt;            cells.clear();&lt;br /&gt;            for (int n = 0; n &amp;lt; 9; n++) {&lt;br /&gt;              int x = (grid%3) * 3 + (n%3);&lt;br /&gt;              int y = (grid/3) * 3 + (n/3);&lt;br /&gt;              c = board[x][y];&lt;br /&gt;              if (c.number == 0) cells.add(c);&lt;br /&gt;            }&lt;br /&gt;            if (checkEmpties(cells)) cellAdded = true;&lt;br /&gt;          }&lt;br /&gt;        }&lt;br /&gt;      } catch (Exception e) {&lt;br /&gt;        prompt(e.getMessage());&lt;br /&gt;        return;&lt;br /&gt;      }&lt;br /&gt;    }&lt;br /&gt;    if (!cellAdded) {&lt;br /&gt;      prompt("Could not solve");&lt;br /&gt;      return;&lt;br /&gt;    }&lt;br /&gt;  }&lt;br /&gt;  prompt ("Solved!");&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;There are a few modifications implied by these methods that I haven't discussed, and many improvements that could still be made. I might continue to work on the bot to clean up code, improve the object model, and handle the backtracking solving method I referred to earlier, or I may leave it as is. Either way, &lt;a href="http://curtisquarterly.blogspot.com/2010/07/working-sudoku-solver-applet.html"&gt;here&lt;/a&gt; is a link to the working applet as it stands, &lt;a href="http://sites.google.com/site/ceauterytest/Home/Sudoku.java?revision=6"&gt;here&lt;/a&gt; is the main applet's source code, and &lt;a href="http://sites.google.com/site/ceauterytest/Home/SudokuCell.java?revision=3"&gt;here&lt;/a&gt; is the cell object source code.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11885757-6723515774778817279?l=cautery.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://cautery.blogspot.com/feeds/6723515774778817279/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://cautery.blogspot.com/2010/09/building-java-applet-sudoku-bot.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11885757/posts/default/6723515774778817279'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11885757/posts/default/6723515774778817279'/><link rel='alternate' type='text/html' href='http://cautery.blogspot.com/2010/09/building-java-applet-sudoku-bot.html' title='Building a Java Applet Sudoku Bot'/><author><name>Curtis Autery</name><uri>https://profiles.google.com/107677530285177731535</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh4.googleusercontent.com/--J3C_khtLqQ/AAAAAAAAAAI/AAAAAAAADT4/e2Qa5ncfAMk/s512-c/photo.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh5.ggpht.com/_YH1HTjXGzNU/TBkrGHuPIOI/AAAAAAAADAw/2ycQOfBO_5w/s72-c/HWApplet.PNG' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11885757.post-3652649489985659590</id><published>2010-09-26T18:25:00.001-04:00</published><updated>2010-09-26T18:29:46.909-04:00</updated><title type='text'>Java Swing applet to calculate Fibonacci numbers</title><content type='html'>I understand this is a common skill builder in Java courses. To write this from scratch, you need to understand Swing input fields, layout management, action listeners, error trapping, and a little math. It also helps if you already think the Fibonacci sequence is cool.&lt;br /&gt;&lt;br /&gt;So to help you seekers of assignment solutions, here you go. Study it, though, rather than just copying it wholesale. Maybe I'm not making this as streamlined as I should, or maybe the xor business is too much trouble for the modest speed and memory boost.&lt;br /&gt;&lt;br /&gt;First, the working applet:&lt;br /&gt;&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;&lt;applet style="background:#ccc;padding:10px;" archive="http://sites.google.com/site/ceauterytest/Home/fib.jar" code="cea/demos/fibonacci/Fibonacci.class" width=400 height=200&gt;&lt;/applet&gt;&lt;br /&gt;&lt;br /&gt;And the code behind it:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;package cea.demos.fibonacci;&lt;br /&gt;&lt;br /&gt;import java.awt.Color;&lt;br /&gt;import java.awt.event.ActionEvent;&lt;br /&gt;import java.awt.event.ActionListener;&lt;br /&gt;&lt;br /&gt;import javax.swing.JApplet;&lt;br /&gt;import javax.swing.JButton;&lt;br /&gt;import javax.swing.JLabel;&lt;br /&gt;import javax.swing.JTextArea;&lt;br /&gt;import javax.swing.JTextField;&lt;br /&gt;&lt;br /&gt;@SuppressWarnings("serial")&lt;br /&gt;// Swing applet to display Fibonacci numbers between a range of integers&lt;br /&gt;public class Fibonacci extends JApplet implements ActionListener {&lt;br /&gt;  // Declare form elements&lt;br /&gt;  JLabel lowLabel, highLabel;&lt;br /&gt;  JTextField lowText, highText;&lt;br /&gt;  JButton button;&lt;br /&gt;  JTextArea output;&lt;br /&gt;  &lt;br /&gt;  public void init() {&lt;br /&gt;    // Set component positions by hand&lt;br /&gt;    setLayout(null);&lt;br /&gt;    &lt;br /&gt;    // Instantiate form elements&lt;br /&gt;    lowLabel = new JLabel("Lower Limit");&lt;br /&gt;    highLabel = new JLabel("Upper Limit");&lt;br /&gt;    lowText = new JTextField();&lt;br /&gt;    highText = new JTextField();&lt;br /&gt;    button = new JButton("Calculate");&lt;br /&gt;    output = new JTextArea();&lt;br /&gt;&lt;br /&gt;    // Set their locations&lt;br /&gt;    lowLabel.setBounds(10, 10, 100, 20);&lt;br /&gt;    lowText.setBounds(10, 30, 50, 20);&lt;br /&gt;    highLabel.setBounds(150, 10, 100, 20);&lt;br /&gt;    highText.setBounds(150, 30, 50, 20);&lt;br /&gt;    button.setBounds(275, 20, 100, 20);&lt;br /&gt;    output.setBounds(10, 60, 365, 100);&lt;br /&gt;&lt;br /&gt;    // Set the text area to properly word wrap, disable it from user input,&lt;br /&gt;    // and set its text color back to black&lt;br /&gt;    output.setLineWrap(true);&lt;br /&gt;    output.setWrapStyleWord(true);&lt;br /&gt;    output.setEnabled(false);&lt;br /&gt;    output.setDisabledTextColor(Color.BLACK);&lt;br /&gt;    &lt;br /&gt;    // Add them to the applet&lt;br /&gt;    add(lowLabel);&lt;br /&gt;    add(lowText);&lt;br /&gt;    add(highLabel);&lt;br /&gt;    add(highText);&lt;br /&gt;    add(button);&lt;br /&gt;    add(output);&lt;br /&gt;    &lt;br /&gt;    // Add action listeners to the button and the two text fields&lt;br /&gt;    // (in case user just wants to hit enter after typing a number)&lt;br /&gt;    button.addActionListener(this);&lt;br /&gt;    lowText.addActionListener(this);&lt;br /&gt;    highText.addActionListener(this);&lt;br /&gt;  }&lt;br /&gt;  &lt;br /&gt;  // The main Fibonacci calculator&lt;br /&gt;  private void calculate() {&lt;br /&gt;    int low, high;&lt;br /&gt;    // Make sure both text fields are filled in with numbers&lt;br /&gt;    try {&lt;br /&gt;      low = Integer.parseInt(lowText.getText());&lt;br /&gt;      high = Integer.parseInt(highText.getText());&lt;br /&gt;    } catch (NumberFormatException e) {&lt;br /&gt;      output.setText("Both text fields must contain integers");&lt;br /&gt;      return;&lt;br /&gt;    }&lt;br /&gt;    &lt;br /&gt;    // Verify limits are both non-negative, and that "higher" is the larger.&lt;br /&gt;    if (low &amp;lt; 0 || high &amp;lt; 0) {&lt;br /&gt;      output.setText("Neither limit can be negative");&lt;br /&gt;      return;&lt;br /&gt;    }&lt;br /&gt;    if (high &amp;lt;= low) {&lt;br /&gt;      output.setText("Higher limit must be greater than lower limit");&lt;br /&gt;    }&lt;br /&gt;    &lt;br /&gt;    // Init the first two numbers in the sequence, and the first line of output&lt;br /&gt;    int current = 0;&lt;br /&gt;    int next = 1;&lt;br /&gt;    StringBuffer out = new StringBuffer&lt;br /&gt;                ("Fibonacci sequence between " + low + " and " + high + ":\n");&lt;br /&gt;    &lt;br /&gt;    // "started" flag is used to check whether or not to add a comma before&lt;br /&gt;    // printing the current item in the sequence. If started is false, you&lt;br /&gt;    // haven't printed anything yet, so no comma is needed.&lt;br /&gt;    boolean started = false;&lt;br /&gt;    &lt;br /&gt;    // Start calculating the sequence until the high limit is reached...&lt;br /&gt;    while (current &amp;lt;= high) {&lt;br /&gt;      // but don't start printing until the low limit is reached&lt;br /&gt;      if (current &amp;gt;= low) {&lt;br /&gt;        if (started) out.append(", ");&lt;br /&gt;        else started = true;&lt;br /&gt;        out.append(current);&lt;br /&gt;      }&lt;br /&gt;      // Add next to current, then use an xor swap to switch them. This avoids&lt;br /&gt;      // using a third variable and the logic c = a + b, a = b, b = c.&lt;br /&gt;      current += next;&lt;br /&gt;      next ^= current;&lt;br /&gt;      current ^= next;&lt;br /&gt;      next ^= current;&lt;br /&gt;    }&lt;br /&gt;    &lt;br /&gt;    // Finally, print the results in the text area&lt;br /&gt;    output.setText(out.toString());&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  @Override&lt;br /&gt;  public void actionPerformed(ActionEvent e) {&lt;br /&gt;    calculate();&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;}&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11885757-3652649489985659590?l=cautery.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://cautery.blogspot.com/feeds/3652649489985659590/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://cautery.blogspot.com/2010/09/java-swing-applet-to-calculate.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11885757/posts/default/3652649489985659590'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11885757/posts/default/3652649489985659590'/><link rel='alternate' type='text/html' href='http://cautery.blogspot.com/2010/09/java-swing-applet-to-calculate.html' title='Java Swing applet to calculate Fibonacci numbers'/><author><name>Curtis Autery</name><uri>https://profiles.google.com/107677530285177731535</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh4.googleusercontent.com/--J3C_khtLqQ/AAAAAAAAAAI/AAAAAAAADT4/e2Qa5ncfAMk/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11885757.post-7240010479444854991</id><published>2010-09-25T09:44:00.002-04:00</published><updated>2010-09-26T05:55:20.515-04:00</updated><title type='text'>Concise Perl</title><content type='html'>One of my favorite tech blogs is the relatively new &lt;a href="http://blog.ksplice.com/"&gt;Ksplice Software Blog&lt;/a&gt;, recently made popular with its unique solution to the recent Linux kernel exploit. In late May they posted an entry called &lt;a href="http://blog.ksplice.com/2010/05/top-10-perl-one-liner-tricks/"&gt;The top 10 tricks of Perl one-liners&lt;/a&gt;, which brought back a slew of memories from several years ago when I was doing lots of system admin work from a Unix shell prompt, and also taught me a regular expression switch I had never come across before: \K. Commandline junkies being who they are, there was also ample discussion in the comments on everyone's favorite trick or idiom.&lt;br /&gt;&lt;br /&gt;A few days after reading the article, I brought my eldest daughter, then 13, to work with me. She recently started high school, and was hanging out with me in the office completing a summer book summary assignment for an honors class due the first day of school. "Dad, what does 'concise' mean?" she asked me, while looking at the instructions. I replied something to the effect that it meant expressing a lot of detail in as few words as possible. The exchange reminded me of the above blog article, and also of some perl mojo I whipped up a couple weeks earlier at work.&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;&lt;b&gt;Background&lt;/b&gt;&lt;/blockquote&gt;&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;In our production Unix environment, we have shell accounts with read access to config files. We also have automatically logged superuser access for emergencies, under the conditions that we use it as infrequently as possible for critical problems only, and that we document the hell out of what we did and why for peer/management review.&lt;br /&gt;&lt;br /&gt;In those situations, it is bad mojo to use a file editor when you're a superuser, as the logging mechanism is text-based, so the reviewer will see a bunch of garbage in the log representing your editing keystrokes. Instead we try to do everything with basic shell commands. Anything more complicated requires opening and documenting a change request, coordinating with the change control team, getting approval... time consuming if you're trying to take care of a hot issue.&lt;br /&gt;&lt;br /&gt;As a hedge between the basic shell commands and the change request, I've found that using a perl one-liner is acceptable, provided I can explain what it is doing. The reason I love perl is that it can be very &lt;em&gt;concise&lt;/em&gt; and expressive, a good tool for tackling many problems that come up at work.&lt;br /&gt;&lt;br /&gt;The situation I was faced with dealt with an MQ queue, and a config file that determined what services were subscribed to it. I received a call about a queue being backed up, so I took a peek at the config file of the joiner that watches the queue, to see where it was trying to write the queue's data to. I tested all the destination addresses, and one of them was invalid (retired without my group being notified, as it turned out). So I needed a quick way to comment out that data sink in an xml config file, but unfortunately the xml file already had comments within the data sink itself. To illustrate:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&amp;lt;sink&amp;gt;&lt;br /&gt;    &amp;lt;type&amp;gt;db&amp;lt;/type&amp;gt;&lt;br /&gt;    &amp;lt;driver&amp;gt;oracle.jdbc.driver.OracleDriver&amp;lt;/driver&amp;gt;&lt;br /&gt;    &amp;lt;url&amp;gt;jdbc:oracle:thin:@(server):(port):(dbName)&amp;lt;/url&amp;gt;&lt;br /&gt;    &amp;lt;!--&lt;br /&gt;      &amp;lt;user&amp;gt;old user&amp;lt;/user&amp;gt;&lt;br /&gt;      &amp;lt;password&amp;gt;old password&amp;lt;/password&amp;gt;&lt;br /&gt;    --&amp;gt;&lt;br /&gt;    &amp;lt;user&amp;gt;new user&amp;lt;/user&amp;gt;&lt;br /&gt;    &amp;lt;password&amp;gt;new password&amp;lt;/password&amp;gt;&lt;br /&gt;    &amp;lt;insert&amp;gt;&lt;br /&gt;        &amp;lt;table&amp;gt;owner.tableName&amp;lt;/table&amp;gt;&lt;br /&gt;        &amp;lt;value&amp;gt;field 1&amp;lt;/value&amp;gt;&lt;br /&gt;        &amp;lt;value&amp;gt;field 2&amp;lt;/value&amp;gt;&lt;br /&gt;        &amp;lt;value&amp;gt;...etc&amp;lt;/value&amp;gt;&lt;br /&gt;    &amp;lt;/insert&amp;gt;&lt;br /&gt;&amp;lt;/sink&amp;gt;&lt;/pre&gt;&lt;br /&gt;In the above xml snippet (sanitized to remove company info) the "old user" and "old password" section has been commented out with &amp;lt;!-- and --&amp;gt;, the xml comment block. What I want to do is comment out the entire section, like so:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&amp;lt;!-- &amp;lt;sink&amp;gt;&lt;br /&gt;    ...lines omitted&lt;br /&gt;    &amp;lt;!--&lt;br /&gt;      &amp;lt;user&amp;gt;old user&amp;lt;/user&amp;gt;&lt;br /&gt;      &amp;lt;password&amp;gt;old password&amp;lt;/password&amp;gt;&lt;br /&gt;    --&amp;gt;&lt;br /&gt;    ...lines omitted&lt;br /&gt;&amp;lt;/sink&amp;gt; --&amp;gt;&lt;/pre&gt;&lt;br /&gt;This is now invalid xml, according to the specs. From &lt;a href="http://www.w3.org/TR/2008/REC-xml-20081126/#sec-comments"&gt;the W3C xml specification&lt;/a&gt;:&lt;br /&gt;&lt;br /&gt;&lt;em&gt;For compatibility, the string " -- " (double-hyphen) must not occur within comments.&lt;/em&gt;&lt;br /&gt;&lt;br /&gt;Nested comments are illegal, then, according to the specs. So not only did I need to wrap the &amp;lt;sink&amp;gt; block in comment tags, I needed to remove the instances of -- that occur within it. Enter perl, and the following multi-faceted command:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;perl -i.b -0777e 'for(split/(&amp;lt;si.+?nk&amp;gt;)/s,&amp;lt;&amp;gt;){if(/dbName/){s/--//g;$_="&amp;lt;!-- $_ --&amp;gt;"}print}' config.xml&lt;/pre&gt;&lt;br /&gt;There's a lot going on here, so I'll break it down one step at a time.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;perl -i.b -e '(commands)' config.xml&lt;/pre&gt;&lt;br /&gt;The -i switch instructs perl to perform an in-place edit on the file (config.xml in this case). This means that whatever the script outputs to STDOUT becomes the new file. The ".b" suffix implies the original file will be copied to config.xml.b as a precaution. The -e switch followed by '(commands)' means treat whatever is in single-quotes as an inline script.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;perl -0777e '(commands)' config.xml&lt;/pre&gt;&lt;br /&gt;This is "slurp mode", meaning if anything in the inline script reads from the file, it reads the entire file at once, and assigns the text to the special scalar variable $_.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;for(list or array){(commands)}&lt;/pre&gt;&lt;br /&gt;Iterate through each item of the list in question, assign the item to local scalar $_ within the bracketed commands section. Fortunately, the original $_ representing the entire file doesn't get overwritten when this happens. Behold the power of scope.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;split /(&amp;lt;si.+?nk&amp;gt;)/s, &amp;lt;&amp;gt;&lt;/pre&gt;&lt;br /&gt;The "&amp;lt;&amp;gt;" reads from STDIN and assigns the input to $_. Since we're in slurp mode, and STDIN is config.xml, this single instruction reads the entire file. The "split" command returns a list, delimiting $_ by whatever regular expression is passed to it. Surrounding the regex with parentheses will include the delimiters as elements of the list as well.&lt;br /&gt;&lt;br /&gt;The regex /&amp;lt;si.+?nk&amp;gt;/ is where the mojo really happens. This grabs a chunk of text starting with "&amp;lt;si" plus at least one more character, ending with the first occurrence of "nk&amp;gt;". Since the xml file in question contains no other tags that end with "nk", this grabs an entire &amp;lt;sink&amp;gt; to &amp;lt;/sink&amp;gt; block. (Why doesn't it just grab the opening &amp;lt;sink&amp;gt; tag and stop? Because of the mandated extra character between i and n.)&lt;br /&gt;&lt;br /&gt;So the complete split command reads in the entire file, splits it into sections delimited by &amp;lt;sink&amp;gt; blocks, and feeds the sections and the sink blocks into the for loop, executing the {(commands)} block on each element of the list.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;if(/dbName/){s/--//g;$_="&amp;lt;!-- $_ --&amp;gt;"}print&lt;/pre&gt;&lt;br /&gt;This is where the text manipulation happens. If whatever $_ chunk I'm looking at contains "dbName", search for all occurrences of "--" and delete them. When finished, replace the entire block with "&amp;lt;!-- ", followed by the old block value, followed by " --&amp;gt;". The final print command outputs the current state of $_. If "dbName" wasn't included in the text, no manipulation happens.&lt;br /&gt;&lt;br /&gt;Combining all these elements together, the file is read, divvied up into sink blocks and surrounding text, and any block containing dbName gets its comments removed, and enclosed in one giant comment block. All in 102 characters. That is the definition of concise. Running the command on the config file yielded the results I was hoping for, correcting the problem with the backed-up queue:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;$ diff config.xml.b config.xml&lt;br /&gt;18c18&lt;br /&gt;&amp;lt; &amp;lt;sink&amp;gt;&lt;br /&gt;---&lt;br /&gt;&amp;gt; &amp;lt;!-- &amp;lt;sink&amp;gt;&lt;br /&gt;22c22&lt;br /&gt;&amp;lt;     &amp;lt;!--&lt;br /&gt;---&lt;br /&gt;&amp;gt;     &amp;lt;!&lt;br /&gt;25c25&lt;br /&gt;&amp;lt;     --&amp;gt;&lt;br /&gt;---&lt;br /&gt;&amp;gt;     &amp;gt;&lt;br /&gt;34c34&lt;br /&gt;&amp;lt; &amp;lt;/sink&amp;gt;&lt;br /&gt;---&lt;br /&gt;&amp;gt; &amp;lt;/sink&amp;gt; --&amp;gt;&lt;br /&gt;$&lt;/pre&gt;&lt;br /&gt;I won't touch on perl too much in this blog since I'm focusing these days on mainly Java, but I encourage you to, if nothing else, explore regular expressions; they're very useful, and I'm continually surprised when I find my coding peers aren't familiar with them. The KSplice blog entry from above is a nice intro to what commandline perl is capable of, and I also wrote a mid-level perl &lt;a href="http://curtisquarterly.blogspot.com/2005/05/perl-tutorial.html"&gt;tutorial&lt;/a&gt; a few years ago in my &lt;a href="http://curtisquarterly.blogspot.com/"&gt;Curtis Quarterly&lt;/a&gt; blog, where I keep odds and ends that don't have a more appropriate home.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11885757-7240010479444854991?l=cautery.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://cautery.blogspot.com/feeds/7240010479444854991/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://cautery.blogspot.com/2010/09/concise-perl.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11885757/posts/default/7240010479444854991'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11885757/posts/default/7240010479444854991'/><link rel='alternate' type='text/html' href='http://cautery.blogspot.com/2010/09/concise-perl.html' title='Concise Perl'/><author><name>Curtis Autery</name><uri>https://profiles.google.com/107677530285177731535</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh4.googleusercontent.com/--J3C_khtLqQ/AAAAAAAAAAI/AAAAAAAADT4/e2Qa5ncfAMk/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11885757.post-8323634905900051477</id><published>2010-09-20T16:32:00.001-04:00</published><updated>2010-09-20T16:34:38.967-04:00</updated><title type='text'>Detecting when a mouse cursor is near a line</title><content type='html'>Warning, this post contains math!&lt;br /&gt;&lt;br /&gt;First, I'm abandoning all of my "recreational" coding projects save one: a java applet for my wife to design clothes. Building a CAD system from the ground up is painful and slow, and I can't tinker any further with other fun things without sapping creative energy I need for my day job.&lt;br /&gt;&lt;br /&gt;In the current early stages of this project, I've been thinking a lot about design, and what I do and don't like about similar programs. One of the things I don't like about simple CAD-like tools is their approach to manipulating existing lines. The line's endpoints are draggable, and so is a single point in the middle, but the line itself is not. Take this screenshot of Visio, for example:&lt;br /&gt;&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;&lt;img src="http://lh3.ggpht.com/_YH1HTjXGzNU/TJeNZj754nI/AAAAAAAADFk/GgfQGlU5x6Q/s800/resize.PNG"&gt;&lt;br /&gt;&lt;br /&gt;I can't click on the line itself and move it, I have to grab the box in the middle of the line. The reason this design style is prevalent has a little to do with object oriented design, and a little to do with math. It is trivial to create an object, give it a bounding rectangle, and have a trigger that fires when the mouse cursor enters the rectangle. It is seemingly difficult, at least for those not willing to crunch equations, to detect when the mouse is near a line set at an arbitrary angle. So the design pattern that wins out is finding the middle point of a line, drawing a box there, and making that box an active component.&lt;br /&gt;&lt;br /&gt;Wanting to be different, as that is my way, I decided the line itself should be hot, and the user could click anywhere within a few pixels of the line to drag it.&lt;br /&gt;&lt;br /&gt;My middle school Algebra and high school Trig classes taught me equations that I found interesting, so I learned them, but like many of my peers, I was skeptical that there was any actual utility in them. They are, in no particular order:&lt;br /&gt;&lt;br /&gt;y = mx + b, describing a straight line with slope m and y-intercept b&lt;br /&gt;x&lt;sup&gt;2&lt;/sup&gt; + y&lt;sup&gt;2&lt;/sup&gt; = r&lt;sup&gt;2&lt;/sup&gt;, describing a circle whose center is on the origin&lt;br /&gt;Lastly, the quadratic formula. If Ax&lt;sup&gt;2&lt;/sup&gt; + Bx + C = 0, then:&lt;br /&gt;&lt;br /&gt;&lt;img src="http://lh6.ggpht.com/_YH1HTjXGzNU/TJeNZOUOUBI/AAAAAAAADFc/4f_OM0sFMjA/s800/quadraticFormula.png"&gt;&lt;br /&gt;&lt;br /&gt;This small snippet from the formula:&lt;br /&gt;&lt;img src="http://lh4.ggpht.com/_YH1HTjXGzNU/TJeNZftDn9I/AAAAAAAADFg/1p401EvWlB0/s800/quadraticSnippet.png"&gt;&lt;br /&gt;can be used to tell me whether or not a line and a circle intersect.&lt;br /&gt;&lt;br /&gt;What does this have to do with cursors touching a line? Can't I just see if the cursor point is on the line by plugging it into y = mx + b? That would create a few problems. First, the user would have no wiggle room, the cursor would have to be exactly touching a one pixel thin line. Second, mouse position is defined in integers, and any non-integer points on the line would not be accessible. Third, if the shape is drawn with anything but a basic one pixel stroke, where in the line to place the cursor would be ambiguous.&lt;br /&gt;&lt;br /&gt;Considering those problems, it is better to define the cursor's location as a circle with a small radius (say, 5 pixels), and use math to check for line/circle intersections. So instead of plugging the cursor point into the line equation, the line can be plugged into the circle's equation.&lt;br /&gt;&lt;br /&gt;Circle on the origin point:&lt;br /&gt;x&lt;sup&gt;2&lt;/sup&gt; + y&lt;sup&gt;2&lt;/sup&gt; = r&lt;sup&gt;2&lt;/sup&gt;&lt;br /&gt;&lt;br /&gt;Arbitrary non-vertical line: y = mx + b&lt;br /&gt;&lt;br /&gt;Substitute line into circle: x&lt;sup&gt;2&lt;/sup&gt; + (mx + b)&lt;sup&gt;2&lt;/sup&gt; = r&lt;sup&gt;2&lt;/sup&gt;&lt;br /&gt;&lt;br /&gt;Multiply out: x&lt;sup&gt;2&lt;/sup&gt; + m&lt;sup&gt;2&lt;/sup&gt;x&lt;sup&gt;2&lt;/sup&gt; + 2mbx + b&lt;sup&gt;2&lt;/sup&gt; = r&lt;sup&gt;2&lt;/sup&gt;&lt;br /&gt;&lt;br /&gt;Solve for 0: x&lt;sup&gt;2&lt;/sup&gt; + m&lt;sup&gt;2&lt;/sup&gt;x&lt;sup&gt;2&lt;/sup&gt; + 2mbx + b&lt;sup&gt;2&lt;/sup&gt; - r&lt;sup&gt;2&lt;/sup&gt; = 0&lt;br /&gt;&lt;br /&gt;In quadratic form: (m&lt;sup&gt;2&lt;/sup&gt; + 1)x&lt;sup&gt;2&lt;/sup&gt; + (2mb)x + (b&lt;sup&gt;2&lt;/sup&gt; - r&lt;sup&gt;2&lt;/sup&gt;) = 0&lt;br /&gt;&lt;br /&gt;Now we have the A, B, and C of the quadratic formula. From there, more math can be applied to find the exact positions of the intersection point(s), but I don't care about that, only whether or not an intersection takes place. That can be determined simply by what type of result the square root function returns. The result types mean the following:&lt;br /&gt;&lt;br /&gt;Imaginary number: No intersection.&lt;br /&gt;Zero: Circle and line are tangent.&lt;br /&gt;Positive number: Circle and line intersect at two secant points.&lt;br /&gt;&lt;br /&gt;Rather than actually perform the square root, we can just remember that the square root of a negative number is imaginary. So if B&lt;sup&gt;2&lt;/sup&gt; - 4AC &gt; 0, there is an intersection.&lt;br /&gt;&lt;br /&gt;Some further formula manipulation gives us this:&lt;br /&gt;(2mb)&lt;sup&gt;2&lt;/sup&gt; - 4(m&lt;sup&gt;2&lt;/sup&gt;+1)(b&lt;sup&gt;2&lt;/sup&gt; - r&lt;sup&gt;2&lt;/sup&gt;) &gt; 0&lt;br /&gt;Divide by 4: (mb)&lt;sup&gt;2&lt;/sup&gt; - (m&lt;sup&gt;2&lt;/sup&gt; + 1)(b&lt;sup&gt;2&lt;/sup&gt; - r&lt;sup&gt;2&lt;/sup&gt;) &gt; 0&lt;br /&gt;Multiply out: (mb)&lt;sup&gt;2&lt;/sup&gt; - ((mb)&lt;sup&gt;2&lt;/sup&gt; + b&lt;sup&gt;2&lt;/sup&gt; - (mr)&lt;sup&gt;2&lt;/sup&gt; - r&lt;sup&gt;2&lt;/sup&gt;) &gt; 0&lt;br /&gt;Simplify: (mb)&lt;sup&gt;2&lt;/sup&gt; - (mb)&lt;sup&gt;2&lt;/sup&gt; - b&lt;sup&gt;2&lt;/sup&gt; + (mr)&lt;sup&gt;2&lt;/sup&gt; + r&lt;sup&gt;2&lt;/sup&gt; &gt; 0&lt;br /&gt;Simplify: -b&lt;sup&gt;2&lt;/sup&gt; + (mr)&lt;sup&gt;2&lt;/sup&gt; + r&lt;sup&gt;2&lt;/sup&gt; &gt; 0&lt;br /&gt;Add b&lt;sup&gt;2&lt;/sup&gt;: (mr)&lt;sup&gt;2&lt;/sup&gt; + r&lt;sup&gt;2&lt;/sup&gt; &gt; b&lt;sup&gt;2&lt;/sup&gt;&lt;br /&gt;Reduce: (m&lt;sup&gt;2&lt;/sup&gt; + 1)r&lt;sup&gt;2&lt;/sup&gt; &gt; b&lt;sup&gt;2&lt;/sup&gt;&lt;br /&gt;&lt;br /&gt;Then, with a circle on the origin point whose radius I know, and a line whose slope and y-intercept I know, I can easily tell if an intersection happens. The obvious drawback here is that the circle, being the mouse cursor area, is in motion, and won't be on the origin. Adding in the h and k modifiers to the circle equation makes the above math more complicated, so instead the y-intercept can be adjusted based on the circle's actual center point, like so:&lt;br /&gt;&lt;br /&gt;double x = line.getX1() - cursor.getX();&lt;br /&gt;double y = line.getY1() - cursor.getY();&lt;br /&gt;double b = y - slope * x; // Y-intercept&lt;br /&gt;&lt;br /&gt;The slope will be calculated once when the line is created to avoid making java do unnecessary math:&lt;br /&gt;slope = (line.getY2() - line.getY1()) / (line.getX2() - line.getX1());&lt;br /&gt;// (m&lt;sup&gt;2&lt;/sup&gt; + 1)r&lt;sup&gt;2&lt;/sup&gt;&lt;br /&gt;range = (slope * slope + 1) * rSquared;&lt;br /&gt;&lt;br /&gt;And then the final comparison is trivial:&lt;br /&gt;return range &gt; b * b;&lt;br /&gt;&lt;br /&gt;Below is a simple java applet as a proof of concept.  Click two points, and a line will be drawn between them. When the line is active, hovering over it will change its color to red. While it is red, it can be clicked on to remove it, and you can then draw a new line.&lt;br /&gt;&lt;br /&gt;&lt;applet style="background:#ccc;padding:10px;" archive="https://sites.google.com/site/ceauterytest/Home/Intersect.jar" code="cea/demos/Intersect.class" width=480 height=360&gt;&lt;br /&gt;&lt;/applet&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;After taking most of a day (I'm ashamed to admit) to work that out, the fun really starts when I try to apply a similar principle to arbitrary Bezier curves!&lt;br /&gt;&lt;br /&gt;Source code for the above:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;package cea.demos;&lt;br /&gt;&lt;br /&gt;import java.applet.Applet;&lt;br /&gt;import java.awt.BasicStroke;&lt;br /&gt;import java.awt.Color;&lt;br /&gt;import java.awt.Graphics;&lt;br /&gt;import java.awt.Graphics2D;&lt;br /&gt;import java.awt.RenderingHints;&lt;br /&gt;import java.awt.event.MouseEvent;&lt;br /&gt;import java.awt.event.MouseListener;&lt;br /&gt;import java.awt.event.MouseMotionListener;&lt;br /&gt;import java.awt.geom.Line2D;&lt;br /&gt;import java.awt.geom.Point2D;&lt;br /&gt;&lt;br /&gt;@SuppressWarnings("serial")&lt;br /&gt;public class Intersect extends Applet implements MouseMotionListener, MouseListener {&lt;br /&gt;  private Point2D p;&lt;br /&gt;  private Line2D line;&lt;br /&gt;  private Color color;&lt;br /&gt;  private double slope, range;&lt;br /&gt;&lt;br /&gt;  private final int cursorRadius = 5;&lt;br /&gt;  private final int rSquared = cursorRadius * cursorRadius;&lt;br /&gt;  &lt;br /&gt;  public void init() {&lt;br /&gt;    color = Color.BLACK;&lt;br /&gt;    addMouseListener(this);&lt;br /&gt;    addMouseMotionListener(this);&lt;br /&gt;  }&lt;br /&gt;  &lt;br /&gt;  public void paint(Graphics g1) {&lt;br /&gt;    Graphics2D g = (Graphics2D) g1;&lt;br /&gt;    g.setColor(color);&lt;br /&gt;    g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);&lt;br /&gt;    if (line == null) {&lt;br /&gt;      if (p != null) {&lt;br /&gt;        g.setStroke(new BasicStroke(cursorRadius));&lt;br /&gt;        g.drawRect((int)p.getX(), (int)p.getY(), 1, 1);&lt;br /&gt;      }&lt;br /&gt;    } else {&lt;br /&gt;      g.draw(line);&lt;br /&gt;    }&lt;br /&gt;  }&lt;br /&gt;  &lt;br /&gt;  @Override&lt;br /&gt;  public void mouseClicked(MouseEvent e) {&lt;br /&gt;    if (line == null) {&lt;br /&gt;      if (p == null) {&lt;br /&gt;        p = new Point2D.Double(e.getX(), e.getY());&lt;br /&gt;      } else {&lt;br /&gt;        line = new Line2D.Double(p.getX(), p.getY(), e.getX(), e.getY());&lt;br /&gt;        if (line.getX1() != line.getX2()) {&lt;br /&gt;          slope = (line.getY2() - line.getY1()) / (line.getX2() - line.getX1());&lt;br /&gt;          range = (slope * slope + 1) * rSquared;&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;      } &lt;br /&gt;    } else {&lt;br /&gt;      if (intersects(e.getPoint())) {&lt;br /&gt;        line = null;&lt;br /&gt;        p = null;&lt;br /&gt;        color = Color.BLACK;&lt;br /&gt;      }&lt;br /&gt;    }&lt;br /&gt;    repaint();&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  @Override&lt;br /&gt;  public void mouseMoved(MouseEvent e) {&lt;br /&gt;    if(line == null) return;&lt;br /&gt;    Color oldColor = color;&lt;br /&gt;    color = (intersects(e.getPoint())) ? Color.RED : Color.BLACK;&lt;br /&gt;    if (!color.equals(oldColor)) repaint();&lt;br /&gt;  }&lt;br /&gt;  &lt;br /&gt;  private boolean intersects(Point2D cursor) {&lt;br /&gt;    // Don't check if the cursor isn't in the line's bounding rectangle&lt;br /&gt;    if(!line.getBounds().contains(cursor)) return false;&lt;br /&gt;    &lt;br /&gt;    // Special handling for vertical lines&lt;br /&gt;    if (line.getX1() == line.getX2()) return cursorRadius &gt; Math.abs(cursor.getX() - line.getX1());&lt;br /&gt;&lt;br /&gt;    double x = line.getX1() - cursor.getX();&lt;br /&gt;    double y = line.getY1() - cursor.getY();&lt;br /&gt;    double b = y - slope * x; // Y-intercept&lt;br /&gt;    return range &gt; b * b;&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt; // Unused mouse methods&lt;br /&gt;  @Override public void mousePressed (MouseEvent e) { }&lt;br /&gt;  @Override public void mouseReleased(MouseEvent e) { }&lt;br /&gt;  @Override public void mouseEntered (MouseEvent e) { }&lt;br /&gt;  @Override public void mouseExited  (MouseEvent e) { }&lt;br /&gt;  @Override public void mouseDragged (MouseEvent e) { }&lt;br /&gt;}&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11885757-8323634905900051477?l=cautery.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://cautery.blogspot.com/feeds/8323634905900051477/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://cautery.blogspot.com/2010/09/detecting-when-mouse-cursor-is-near.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11885757/posts/default/8323634905900051477'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11885757/posts/default/8323634905900051477'/><link rel='alternate' type='text/html' href='http://cautery.blogspot.com/2010/09/detecting-when-mouse-cursor-is-near.html' title='Detecting when a mouse cursor is near a line'/><author><name>Curtis Autery</name><uri>https://profiles.google.com/107677530285177731535</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh4.googleusercontent.com/--J3C_khtLqQ/AAAAAAAAAAI/AAAAAAAADT4/e2Qa5ncfAMk/s512-c/photo.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh3.ggpht.com/_YH1HTjXGzNU/TJeNZj754nI/AAAAAAAADFk/GgfQGlU5x6Q/s72-c/resize.PNG' height='72' width='72'/><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11885757.post-4577942075243195103</id><published>2010-09-16T17:21:00.002-04:00</published><updated>2010-09-16T17:21:49.760-04:00</updated><title type='text'>Quick experiment with Java 2D, JComponents, and MouseListeners</title><content type='html'>Here's something I've been toying around with for a couple days as, of all things, a relaxation experiment. Basically when a shape is hollow, you can drag it to change its size. When a shape is solid, dragging positions it. Clicking on a shape without dragging flips it from hollow to solid, and sets its z-order to the top.&lt;br /&gt;&lt;br /&gt;Enjoy!&lt;br /&gt;&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;&lt;applet style="background:#ccc;padding:10px;" archive="https://sites.google.com/site/ceauterytest/Home/shapeApplet.jar" code="cea/demos/shapeApplet.Applet.class" width=640 height=480&gt;&lt;br /&gt;&lt;/applet&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11885757-4577942075243195103?l=cautery.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://cautery.blogspot.com/feeds/4577942075243195103/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://cautery.blogspot.com/2010/09/quick-experiment-with-java-2d.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11885757/posts/default/4577942075243195103'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11885757/posts/default/4577942075243195103'/><link rel='alternate' type='text/html' href='http://cautery.blogspot.com/2010/09/quick-experiment-with-java-2d.html' title='Quick experiment with Java 2D, JComponents, and MouseListeners'/><author><name>Curtis Autery</name><uri>https://profiles.google.com/107677530285177731535</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh4.googleusercontent.com/--J3C_khtLqQ/AAAAAAAAAAI/AAAAAAAADT4/e2Qa5ncfAMk/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11885757.post-8051562318494223964</id><published>2010-09-09T18:37:00.000-04:00</published><updated>2010-09-09T18:37:30.846-04:00</updated><title type='text'>Labor Day adventure weekend, and the death of a friend</title><content type='html'>My wife is a saint. If you have any sort of relationship with Liberty, that may not be the first descriptor that jumps to mind, but I assure you it's true. In fact, there was a time when I believed her assertion that she has no empathy, but now I know that for what it really is: a complete ruse. It's her version of my blank expression that I meet most people with, the thing that's a little off-putting and helps me extricate myself from conversations, the thing that lets me keep my distance instead of getting too involved, and has nothing to do with what's actually going on upstairs.&lt;br /&gt;&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;My problem is that I care a lot about people, and it's far too easy for me to lose myself in their struggles with life, making myself an emotional wreck over their trivial daily dramas. I suspect the same is true of my dear wife, and the "whatever, that's boring, I hope she stumbles out into traffic" she sometimes projects is her coping mechanism for a deep-seated caring for the people in her life that leads to a constant emotional firestorm.&lt;br /&gt;&lt;br /&gt;A few weeks ago the two of us took Dave (ex-mentee, current 21 year old looking for answers, future world conqueror) out to see Inception, and he started out a little nauseous, getting progressively sicker as the movie went on, and about 30 minutes in asked us if we could just drive him home instead. Liberty never hides disappointment or frustration from her face, so I expected this to be sort of a bad scene, but it was anything but. What registered on her face was caring, and an expression of "of course this stupid movie can wait, let's get you out of here so you feel better." Over this weekend, that expression found its way to me.&lt;br /&gt;&lt;br /&gt;We set out early Saturday to head down to Mammoth Cave, and about 2 hours into the drive, I started getting a small headache that I assumed would pass with a soda and a couple aspirin. A little later, I assumed maybe a Claritin and a couple more aspirin would do the trick. Then, no, how about a full dinner, maybe I'm short on protein. OK, 4 more aspirin. Maybe one of your prescription allergy pills, honey - at this point I knew I was in trouble; it was 4 years ago all over again when chronic sinus infections had me worried about the future quality of my life.&lt;br /&gt;&lt;br /&gt;I ended up wracked with pain and seeking the first bed I could find, where I tossed and turned, took a hot shower, tried not to moan or pound my fist in despair and impotent rage, and finally made a trip to a nearby gas station for Tums and Advil (too many pills throughout the day leads to stomach problems, as I hope none of you are intimately familiar with). A few hours later I finally settled down to sleep, waking up about 4 hours later pain free.&lt;br /&gt;&lt;br /&gt;Through it all, Liberty was supporting and loving, without a hint of "you're ruining my vacation, asshole" in her voice or mannerisms. At one point she even lovingly caressed me and said "should we get you to a hospital?" That's a phrase I hope I never hear again on vacation, but I was glad to hear it coming from her because of the implication: your wellbeing is more important than a fun vacation.&lt;br /&gt;&lt;br /&gt;Day two was less exciting as far as my own health goes. I woke up at about 5am, more or less completely healed. The sunglasses I keep in my car had a film of pollen on them, explaining the source of the previous day's misery, and we spent the rest of the trip with the windows up, air conditioning on, and outside air vents closed, which successfully prevented any mishaps.&lt;br /&gt;&lt;br /&gt;Mammoth Cave turned out to involve things neither of us was willing to stomach: tourists, being herded, bratty kids with parents who were, at best, indifferent to their complaints about the unfairness of everything. Nightmare. So we opted for the random drive-and-see-what-happens vacation instead. We ended up in Nashville, stopping at the local Flying Saucer bar (where we both added three beers to our tour), and a museum that was showing some French Couture displays, where Liberty explained all the ways the description copy on the displays was inaccurate, and how the fat American mannequins were stretching the older French clothes, ruining seams and zippers. [This is sort of our idea of fun. When it's my turn, say when we pass a big box store, digital marquee, or automated supermarket checkout line, I let loose a constant stream of "dude couldn't code his way out of a box" comments, decrying bad error checking, unclear user interfaces, and snooty voice actors. It's what we love - don't judge.]&lt;br /&gt;&lt;br /&gt;On the drive back I checked my voice mail, and got the news that my cousin Steve had died. I was stunned. He was 30, and I had just spoken with him a month ago at our family reunion. He was a former marine who was stationed in Iraq for a while. He was dealing with survivor's guilt and PTSD, was on psychoactive medication that made him sleepy, and he had put on a lot of weight. I talked to him away from everyone else at the reunion, where he shared with me some horrible war stories that I won't repeat. Suffice to say, I understand why he felt the way he did.&lt;br /&gt;&lt;br /&gt;Steve and I lived together for a short time when he was 9 and I was 18, and we both lived in my Grandmother's house. I was playing the role of hip older brother-figure. He was fond of bursting into my room with pronouncements or questions, or asking me to come play with him and the neighborhood kids, where I would lead them all into the woods to trek around and get lost, or have mock stick-fights with them. I taught Steve some basic fighting skills when we would spar in the backyard (mainly that position, timing, and distance mattered more than aggression or fancy moves). We would both leap from the second-story balcony onto the uneven, hilly grass below in bravery contests (neither of us breaking anything is nothing short of a miracle). He showed signs of being troubled then, but as much as I loved him and appreciated being looked up to by someone, I was caught up in the petty dramas of being a new adult, and fled back to Ohio to stake my claim on life, and we lost touch.&lt;br /&gt;&lt;br /&gt;I saw him a couple years later when visiting North Carolina, and we went out to see Buffy the Vampire Slayer - the movie, not the show. I ended up being a fan of the Joss Whedon series, and would often think about Steve when I watched it. Steve seemed to have matured a little, maybe quieted down some, but I only saw in hindsight that things weren't going well for him.&lt;br /&gt;&lt;br /&gt;His life was hard after I left. He joined up with a bad crowd in school, became obsessed with being cool and looking tough, and once angrily pushed my grandmother out of the way in a family dispute. His mother was, frankly, not doing her job, frequently throwing her hands in the air and complaining "I cayn't do nuthin with 'im!" and the responsibility of trying to raise Steve fell to my grandmother. In her 70s, she didn't have the skills or strength needed to deal with a kid hell-bent on being trouble. After a close friend of his from the "bad crowd" was shot and killed, my family intervened more forcefully, and got him into a reform school in another state.&lt;br /&gt;&lt;br /&gt;I next saw him when he was 16 and I was newly married to my first wife. I gave him one good lick for having pushed my grandmother, which squared things between us (if you are familiar with Southern culture, you'll see that this is a formality which can't be overlooked - it would be improper). He embraced the lifestyle of bad-kid-turned-good, and I was happy to see that he was safe, free of the weapons or drugs that would naturally have come next had he not been moved to the new school, more polite, and less angry. He still had some of the tough-guy look, but as someone intimately familiar with anger, I could see his heart really wasn't in it.&lt;br /&gt;&lt;br /&gt;From then on I only heard a few passing stories of his progress. He latched onto religion for a while, and seemed destined to become a pastor or motivational speaker ("How I turned away from evil" or something). He ended up joining the military, which I found a little surprising, but it would have naturally appealed to his sense of honor and penance.&lt;br /&gt;&lt;br /&gt;When we talked at the reunion, I had no idea that would be the last time we would speak, but I don't think I would have done anything differently. I gave him a listening ear, let him know that I still gave a damn what happened to him, and we made tentative plans to meet in Cincinnati later this year when he was scheduled to visit a clinic there to help manage his PTSD. Instead he died. In his sleep. Where his pregnant wife and 3 year old daughter found they couldn't rouse him from his nap.&lt;br /&gt;&lt;br /&gt;I thought about all this as Liberty and I were driving back from our adventurous weekend trip, and after the initial shock wore off, I found that I didn't feel much of anything. Like all emotional wounds that cut deep, the pain came later. Last night, in fact. I was browsing various sites about sending care packages to deployed troops, and it hit me like a ton of bricks. It was all I could do to not burst into tears in front of my wife.  If you're familiar with Southern culture, you'll understand that you don't cry over a dead man in front of your wife. It would be improper.&lt;br /&gt;&lt;br /&gt;So Steve and I were close at one time, and our lives were skew to each other after that. But I loved him. And it hurts. And he deserved more life.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11885757-8051562318494223964?l=cautery.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://cautery.blogspot.com/feeds/8051562318494223964/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://cautery.blogspot.com/2010/09/labor-day-adventure-weekend-and-death.html#comment-form' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/11885757/posts/default/8051562318494223964'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/11885757/posts/default/8051562318494223964'/><link rel='alternate' type='text/html' href='http://cautery.blogspot.com/2010/09/labor-day-adventure-weekend-and-death.html' title='Labor Day adventure weekend, and the death of a friend'/><author><name>Curtis Autery</name><uri>https://profiles.google.com/107677530285177731535</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh4.googleusercontent.com/--J3C_khtLqQ/AAAAAAAAAAI/AAAAAAAADT4/e2Qa5ncfAMk/s512-c/photo.jpg'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-11885757.post-4445315739204892156</id><published>2010-08-26T15:22:00.003-04:00</published><updated>2010-08-28T07:36:07.553-04:00</updated><title type='text'>Fantasy RPG applet in Java, the early stages</title><content type='html'>For the past week I've been dabbling at home with building an old school Fantasy RPG, and I've been enjoying the hell out of it. It isn't playable yet by a longshot, as so far I've only got a  combat framework and map scroller functional. For what the first real build of this will look like, think Bard's Tale 2. Think Ultima 4. At least, that's what I'm thinking now, as turning this into Diablo or World of Warcraft is still a long way off.&lt;br /&gt;&lt;br /&gt;I got the itch to design a game after toying around with Google's App Engine framework. I wrote a slideshow unwrapper for my wife to use when browsing on Ebay or Etsy, which lets you paste in several URLs with slideshows in them, and builds a single page containing all the images. App Engine's "fetch url" function and the ability to execute arbitrary Java code made that a snap. While learning the App Engine ropes, I found that it can serve an applet, and receive an arbitrary serialized object and store it in Google's "data store".&lt;br /&gt;&lt;br /&gt;In other words, you can play a game in an applet, and save the game state to the data store.&lt;br /&gt;&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;I don't think that can be extended to be an MMORPG, since keeping ports open for always-on two way communication isn't supported, but something approaching a MUD wouldn't be too far fetched.&lt;br /&gt;&lt;br /&gt;As it turns out, Java objects extend perfectly into defining RPG concepts. Stats (strength, dex, hit points, etc.) can be an object. "Item" can be an abstract class, where Armor, Weapon, and Scroll can be concrete extensions of Item. "Action" can be abstract, with Script, Combat, and Retail as concrete extensions. Fun stuff. Here is a screenshot of an early experiment with combat where a level 3 player with a sword and shield goes to battle with a level 1 skeleton armed, sadly, with only a bone:&lt;br /&gt;&lt;br /&gt;&lt;img border="0" src="http://lh3.ggpht.com/_YH1HTjXGzNU/THPLhyloxFI/AAAAAAAADEE/k4K4NXlAHOw/s800/loreFirstCombat.PNG" /&gt;&lt;br /&gt;&lt;br /&gt;For displaying the player on a world map, I want to leave him centered while the world moves around him. Right now this is all simple icon-based with no animation (the first couple screenshots below show only solid color icons, the last is using the icon set from Ultima 4), but that can be extended if I ever get the game mechanics fleshed out and complete. Knowing Jack about animation, that should be fun if I ever get there.&lt;br /&gt;&lt;br /&gt;The scrolling turned out to be an interesting puzzle. I have an image representing the world map, and superimposed on that is the player icon, it whatever position he happens to be in the world
