Þ   briarpig  » log  » apr09


Apr 2009 This way to Apr 2009 entries.

demos

     Lately I write demos under thorn with a todo list using C++ under a BriarPig mu-babel license. The run and hex demos were done on 13apr2008; crc on 14apr2008; buf on 20apr2008; in on 27apr2008; ctype on 04may2008; out on 18may2008; slice on 25may2008; quote on 31may2008; escape on 31may2008; mutex on 14jun2008; rand on 16jun2008; stat on 17jun2008; primes on 19jun2008; list on 23jun2008; heap on 29jun2008; iter on 02jul2008 and 04jul2008; atomic on 06jul2008; node on 13jul2008; page on 23jul2008; hash on 27jul2008; book on 27jul2008; pile on 03aug2008; stack on 07aug2008; mu on 12aug2008; toy on 16aug2008; weight on 23aug2008; symbol on 02sep2008; imm on 04sep2008; gc on 06sep2008; map on 14sep2008; meter on 16sep2008; thread on 22oct2008; arc on 05nov2008; this menu links all demos:


     mu: toy, peg, imm, tag, box, symbol, token, number, bigint, class, method, reader, writer, eval, env, vm, gc, world, pcode, compiler, asm, lathe, lisp, smalltalk, design, weight, jar, card, harp, debug, profile

     thorn: todo, names, iovec, assert, log, run, hex, crc, buf, in, out, quote, escape, compare, file, deck, cow, arc, blob, tree, slice, rand, time, stat, heap, node, primes, page, book, pile, stack, atomic, lock, mutex, thread, map, list, iter, ctype


30apr09 ring operas

origin story

     And that's how Zé gets the ring. Just so you know, when I wrote the scene where Ishtar throws him something small, I had no idea what it was. I planned only to come up with something. As I thought about it more, it became very interesting to me, and I fell in love with some back story in mythology and history (both past and future history). I wrote a lot of that as almost continuous narrative so I could land the provenance of Urd just enough so it sticks.

     I've been thinking about whether I should write what happens next simply by continuing from there, but I have several reasons not to do so. For one thing, it's really complex getting them out of Wil's apartment. Flywheel patches through a voice call by Mick, who has a fun chat with Finch about how nuts she is to still be in there.

     Another good reason to jump ahead: to withhold scenes introducing Mick and Aleph. They're important—establishing what everyone thinks of one another, with lots of ambiguous side play to open questions about what understandings are not in the clear. So I've been working through these scenes, which involve a lot of dialog. I know a lot more about what Mick and Aleph are like. Aleph in particular is going to be fun to write. (She was going to use the nickname Alf until I noticed it was only one letter different from Ulf, which seemed like a bad idea.)

     Anyway, I'd rather give you something to figure out. So the most interesting scenes, ones starting a trajectory, I'd rather use to inform later scenes. You might notice I've only written a couple scenes where characters meet someone new the first time: when Wil and Zé meet Eli, and when Zé meets Ishtar. The former was partly characterization of Eli: a noob subjected to an interview even when it seems unnecessary. He's just young enough that one might hope to get a grip on what he's like in a first meeting. (No such luck with Mick and Aleph.) And in the scene where Zé meets Ishtar, it's impossible to learn much. Any other first meeting you can just interpolate as you like.

     So, um, I'm thinking about making the story disjointed in time flow now, with a lot of jumping around and a few flashbacks when I feel like it. I like this sort of effect myself, even when it doesn't help convey feelings Wil and Zé get of not understanding situations well. Jumping around helps you simulate their sense of imbalance.

     I'm also avoiding a mistake I see even professional writers make: trying to spell out every detail just because it's feasible. Since you can't render everything in the same level of detail, too much detail in one place actually breaks the illusion—it highlights the granularity too much. Here's an example from visual arts, which might help you understand. If you render all parts of an image in fuzzy detail—in a pointillist style, say—it cues an observer to gloss over all the parts at the same uniform level. A single part of an image done in too much detail can make all the rest look inferior by comparison. Or in writing, when you write in a fast tempo, a brief treatment of most things can prompt a reader to squint the same amount everywhere, to make up for lack of skill in a writer. (I don't pretend to much skill.)

     I also want to jump around so I can treat scenes with different levels of detail: some very short, but other scenes much longer. At times this may look like different characters telling their part of the story—an effect I think will complement the plot. I have a vivid scene in mind next for Ulf, taking place in Professor Marvel's wagon after Ulf wakes up still inside the dream. This is sufficiently weird that it may unbalance the surrounding story unless things already get more loosely structured. (But I need to do it, because what happens next fleshes out another character. And parts of it gave me goosebumps.)

     Ulf's dream also helps establish a partially real environment, setting a precedent for what happens in Zé's dream quest. I think interleaving the dream quest with 'real' scenes involving Wil may work well.

     No, actually, I do like a couple authors younger than Stephenson. There's one in particular I was going to mention, but I don't want to get into the business of naming names. And a little excessive sex in one or another novel doesn't put me off someone's writing as long as it's clever. It's pretty hard keeping sex out of the story I'm writing. If you're wondering whether it occurs in the mix, well uh, yeah. If I was writing a book I'd go ahead and include it. I plan to let a little creep in soon, since I plan to use jangly structure to help fix all kinds of continuity threats, like strong emotional entanglements.

     The real-ness of Ivy will remain in limbo as long as I can manage. But Wil may swing toward disbelief in the near term. It's a great excuse for kneading the emotional dough, as characters look for patterns inside patterns.

29apr09 zero denominators

pool alloc

     I made a small bug fix in code for row: temp stack row instances need to use the same vat as the row receiving content, or else one row gets references to more than one vat. (And while that works, it wasn't my intention.) I started using a better vat in one context, and saw a row receive fragments in the global vat, and said, hmmm.

virtual file systems

     For testing purposes, you sometimes want to simulate a large file system full of content. For example, one of my simulations needs to pseudo randomly sample complex mixtures of guassian distributions from what looks like a large file system. Except I don't want to bother filling a disk with a zillion gigabytes. So, how do I pretend a large file system is on hand? Ideally, I want a seekable stream I can sample on demand, and any time I read from the same range of bytes, I need to get the same content. It must be generated on demand, in a completely deterministic fashion, and in constant time.

     Last week that ate into one of my evenings until I thought of the answer. I'll tell you the metaphor in a way you'll see why it obviously works. (But actually coding it still has tricky parts.)

     Let's say you fill a four megabyte buffer with pseudo random 32-bit integers from a linear congruential multiplier with a large period. (When the integer used to mod and the multiplier are relatively prime—obviously true when both are prime—then none of the 32-bit values will repeat until the period is exhausted. So in this case, none of the words in the four meg buffer will repeat values.)

     Now, how do you sample this data in a simple, deterministic way to create a huge virtual data set which repeats nowhere in a granularity you are concerned about? For example, suppose you want no two kilobyte fragments to repeat in a data set many gigabytes in size.

     Imagine two large gears, each with a prime number of teeth, P1 and P2. From a starting position where two teeth touch each other, how long can you rotate the gears before the original position comes around again? That's right, you'll see P1*P2 pairs of teeth before you repeat.

     In the four meg buffer, say each 64-byte cache line is one "tooth." (As long as we bother touching a cache line, we might as well take the whole thing.) One gear counts cache line teeth upwards, the other downwards. So we're going to generate a sequence of 128-byte pairs: 64-bytes from one gear and 64-bytes from the other.

     When you divide four megs by 64 bytes, you get 64*1024, or 65536, with teeth numbered from zero to 65535. But we want the two largest primes less than 65536. So we use 65521 and 65519 as the number of teeth in our two gears. This means our stream contains 128 bytes times (65521*65519) before the stream repeats: a half terabyte (unless I dropped a power somewhere).

     Okay, so someone issues a request for N bytes read at offset position X. How do you figure out the teeth to read—the tooth pairs you would see it you turned the gears from zero to offset X? This is easy.

     If offset X is not 128-byte aligned, you need to mask off the low seven bits to find the preceding tooth pair (in which you need to skip the first X&0x7F bytes). The aligned offset A is X&~0x7F, so the starting tooth for one gear is A%65521 and the other is A%65519. Then all you have to do is avoid fencepost errors, and wrap around at the ends. One gear counts downward and wraps at the low end. The other gear counts upward and wraps at the high end.

28apr09 training wheels

context

     (See the fiction page first for context.)

eyes behind

     After watching Flywheel's disembodied head talk to Finch a brief time, Wil noticed he could see through Koi's holographic image—it was slightly transparent. But how did it work? Wil couldn't think of any way to make an image float a foot over that device on the table. And how was Flywheel getting his view of Wil's living room? When Koi looked at Wil, the feeling of eye contact was perfect. So, where was Koi getting his image feed? Sensors in the base? Or somewhere else? They could be anywhere. Tok was studded with them, so Wil could pan Tok's view merely by wanting it to happen. Suddenly Wil had an idea: did his helmet have sensors in the back of his head?

     Wil tried panning his own view without moving. Slowly it started to work, until suddenly Wil looked directly behind without turning—but he was still looking forward, too. The resolution of his rearward view degraded unless he focused. Wil doffed his glasses to see if a view behind was drawn on one of the lenses. But images on the inward side of his glasses cleared instantly. Wil lost some of his rearward view, but not all: he still had partial vision every way he considered. Was he just hallucinating? By turning his head sharply, Wil compared what he perceived with what he actually saw by eyes alone. It gibed with close agreement—interesting. Finch noticed Wil's behavior.

     "Eyes behind?" Finch turned and asked, smiling.

     "Yeah," Wil nodded. "Am I getting a feed from sensors in my helmet? To mites in my brain? Or something?"

     "Sure," Finch nodded. "Feeds from all over, not just helmets. Mites decoding forward sight can map rear views from reflections: you now have 20/10 vision."

     "Can you talk shop later?" Flywheel lamented.

     "I have no privacy at all, do I?" Wil realized.

     Finch wrinkled her nose and shook her head no.

     "That's gonna be a problem," Wil sighed.

     "Let's save the world now and worry about your privacy later, shall we?" Flywheel pleaded.

     "Are we saving the world?" Eli sounded jazzed.

     "From my point of view, yes," Flywheel replied.

     When Eli rolled his eyes Finch explained, "Koi needs practice weighing things the way you do."

     "No I don't," Flywheel objected. "If the horn had blown up, I'd be just as cooked in the long run."

     "So, why didn't the horn blow up?" Zé asked.

     "It should have," Finch considered. "It's supposed to defend itself from subversion. Apparently your ring found a way for that failsafe to malfunction."

     "You keep saying 'find a way' when talking about Urd," Zé nodded at the ring on his finger, which now looked several times brighter than when Ishtar gave it to him late this afternoon. Whatever it was doing to the horn gave subtle signs of high activity. It was a bit warm.

     "Think of yourself as incredibly lucky," Flywheel said. "According to legend, when a Norn first chooses a new host, he or she can't be injured for days, due to what seems insane turns of luck. For example, if Finch would be so good as to aim her gun at your foot and ..."

     "Not on your life," Finch interrupted loudly, holding a hand up to Flywheel's face. "I'm not an idiot."

     "We really should test the theory it's Urd better," Flywheel wheedled. "What's the worst that could happen? You're not afraid of a little ring, are you?"

     "Yes I am, absolutely," Finch countered. "If I was lucky, my gun would simply break, and I'm fond of my gun. At worst, if there's no possible way to fail my gun, Urd might decide to hit me in the head with a boulder-sized meteorite—which could conceivably occur—before I finish aiming my gun. And in my case, since I can dodge, something more drastic can happen. So I repeat: No thank you."

     "Why not give Urd back to Ishtar?" Eli suggested.

     "I love that plan," Flywheel approved.

     "No," Finch shook her head, smiling. "We're going to keep it. This is way too good to pass up. Besides, Zé would never forgive me. You want it, don't you Zé?"

     Zé tried unsuccessfully to suppress a wry smile. "When you say host, what does that mean? After a few days grace period exemption from injury, then what?"

     "You might not get several days," Finch considered. "No one was dumb enough to strap a horn to one before, that I know about. I think it usually takes time to get enough resources in hand to finish construction."

     "Of ... what?" Zé prompted while studying Finch carefully. They stared at one another.

     "The other rings," Finch said finally. "Eight minor rings, in a network inside you."

     "Fuck me," Wil said in awe.

     "You make it sound like a bad thing," Koi joked.

     "What are they for?" Zé asked. "Is that where minor rings originate? Spawned by Norns?"

     "Yeah," Finch tossed her head with a slight attitude. "According to lore anyway. I don't know what they do, but they might act like distributed processors. Each is powerful all by itself, afterward, when the host finally dies. A nuke would do it, if you decided to sit still for it."

     "Hold on," Zé pressed fingers to his temples, breathing rapidly. "I have too many avenues to follow here. Let's start with this scary word: host. Will I be me?"

     "What a good question," Flywheel admired.

     Finch nodded and wagged her index finger. "I know exactly what you're asking," she said with a twisted smile. "Apparently whoever made those rings designed them to augment the wearer. However, Abraham Lincoln once said: Nearly all men can stand adversity, but if you want to test a man's character, give him power."

     "I start to get how you feel about Ishtar," Zé said.

     "You have no idea," Flywheel disagreed. "It's like playing chess with a legendary fiend. She is a legend in ... what are we calling home now?"

     "Thule," Finch sighed. "You give her too much credit. She's not the devil. Just a top notch player."

     Flywheel caught Wil's eye and silently mouthed one word clearly: jealous. Wil snickered.

     "I take it folks who wear Norns are hard to kill?" Zé asked. Finch and Flywheel exchanged glances.

     "Very," Finch confirmed. "If you get past the early phase, that is, when everyone's after you. But I rate your odds high since you have Wil on your side. And me, of course."

     "What's that about?" Flywheel asked Finch.

     "I know something you don't know," Finch taunted.

     "I need to get out of this business," Flywheel said.

     "Who's checked in so far?" Finch asked. "I need someone with a talent for improvisation."

     "Hey, I need some nano armor, too," Zé said.

     "How about Aleph or Izzard?" Flywheel offered.

     "Aleph!" Finch snapped her fingers. "Send that girl over. She's just what Ishtar needs. And someone good with a gun to train Wil while Zé and I start a shell game. Is Mick still close by here?"

     "He's already on the way," Flywheel smiled.

     "You may have already won!" Ulf said loudly from the sofa, tossing in his sleep.

     "Maybe you won a clearing house sweepstakes," Eli told Wil with a smile. "Dad channeled Ed McMahon."

     "Armor," Zé told Finch, drawing slowly closer.

     Finch pointed to Wil's bedroom. "Pale green in your size. Have Eli show you how to get dressed."

     "How heavy is this ring going to get?" Zé turned back and asked. "Is it packing matter tight?"

     "Not much heavier," Finch shrugged. "But you'll gain about ten pounds inside you, mostly in your bones. You won't be able to pass an airline security scan."

     "But by then they won't be able to tell you no," Flywheel said enigmatically.

27apr09 avant garde sins

context

     (See the fiction page first for context.)

enchantment under the sea

     At first Ulf dreamed he was having an out of body experience, watching the others from a vantage point somewhere in Wil's living room, as if he was sitting on the sofa. Lights had been dimmed. A mirrored disco ball cast meandering glints of light spinning slowly around the room as Zé and Finch danced beneath: Zé in a white tux constrasting nicely with Finch's all black, with an old-fashioned top hat perched on her head. Zé danced just like Crispin Glover playing George McFly, but Finch favored a go-go style with arms pumping up and down alternately. When Zé smiled, one tooth gleamed Like Tony Curtis as the good guy in some old movie about a great race; Finch was his opponent and she meant to win—right after this dance anyway. A tornado approached.

     Or it sounded like one: loud rumbling white noise with scattered almost human piercing cries. The ceiling of Wil's apartment came off, lifted by the hand of a giant woman about sixty feet tall, with faerie wings rising from her shoulders. She had red hair and Ulf knew immediately it was Ishtar somehow. Ishtar smiled beautifully, but her eye sockets glowed a strange color that somehow wasn't purple, as if another previously unknown color had come into play, changing everything according to some dire dream logic. As the ceiling peeled aside, the sky above Ishtar was lit by a full moon casting faint rays of light as it spun slowly to accompany human-sized winged beings who flew in circles above, crying out like birds.

     Finch drew a weapon immediately but held her fire during a brief parley Ulf couldn't hear, until a winged creature swooped near; then a beam of light undulated organically from Finch's gun, bursting the creature into flaming shards with a wretched scream. Ishtar leaned in to collect Zé but Finch reached him first, dragging him through a revolving fireplace in Wil's apartment.

     Eli and Wil kept busy taking out flying creatures with their guns. Towering over the scene, Ishtar finally took note of Ulf on the sofa and smiled while asking, "What am I going to do with you, Mr. Varg? I suppose Mr. Ketch can carry you back to my workshop."


     Ulf's dream did a jump cut to a black and white country scene where Professor Marvel's horse-drawn carnival wagon stopped on an embankment next to a wooden bridge. With no one in sight, Ulf entered the wagon but found no one home. On the table rested two crystal balls. Gilded letters floated in the air above each, bringing the only color to this totally grayscale environment.

     "Your Fate," read the letters above one. The other crystal ball said simply, "Tomorrow." Ulf's heart surged when he saw the latter: he had long dreamed of being able to afford this application. Tomorrow. And now here it was, free for him to use. Ulf looked around a moment, wondering if Professor Marvel might come home soon.

     As Ulf sat, Tomorrow began to glow, revealing scenes in technicolor. The first scene was Wil's apartment: Wil sat at his desk working. Ulf groaned—how was this any different than any other day? But then Wil's living room was raked by automatic gunfire. Meg took the brunt of the first long bursts, as if attackers didn't know Meg was just a half-assed robotics project Zé had been nursing since forever. Bits of wire and circuit boards flew.

     Where was Eli? Ulf touched the crystal ball's surface and gave it a twitch with one finger, because obviously this was a multi-touch user interface. Other scenes skimmed by in a slowing crawl just like an iPhone, until it stopped on Eli in a newspaper office, where he had been doing research for a story on Miss Morlock. But he was running through a cube farm in a crouch, pursued by agents with guns looking for him. This looked like a job for Morpheus, but no one delivered a cell phone to Eli for guidance.

26apr09 fleeting weekends

see why

     Okay, I finished a new section on this site: cy clones a lot of my thorn library under a BSD license explained on that page. I wrote all those pages yesterday and today (Saturday and Sunday) and in restrospect it doesn't seem a good way to spend time. (Describing code actually seems to take longer than writing much of the time.)

     A lot of those pages aimed to support a row api specifically. But I ran out of time to write commentary on that page; I'll do it later. My goal was to get the source code up before I clone it one more time at work for use in a quick and dirty prototype exploring a piece of technology I'm trying to characterize (which I can't tell you about).

     Most of the other cy pages have a reasonable amount of commentary—at least the amount I intended to write. The deck page will be nearly identical to row, but using std::vector instead of the cy::Vt class shown on the vector page. In fact, the purpose of row was to start a new revision that does not use standard C++ libraries. Okay, that's done.

25apr09 zombie autopsies

bsd license

     I'm preparing a large amount of code to release under a BSD license. I'm working long hours every day. I hoped I would be done tonight, because I really need to spend tomorrow on tasks for my day job. (Otherwise my schedule is going to creep.)

     Yes, there's a lot more to the story in I have in progress. I just didn't work on it this week. I'm having the most productive coding streak I've had in years. So that's where my time is going.

     The BSD licensed code is just rewrites of my thorn library, plus a few bits I never go around to finishing as a demo. I'm not going to publish much more code. It takes too long. I'd rather split my time between fiction and async programming languag runtimes.

     This open source licensing turn is temporary and limited in scope, indicating I want to wrap up my efforts in coding for online display.

sci-fi taste

     My taste in science fiction is old fashioned. The youngest author I like a lot is Neal Stephenson, and he's fifty years old. If you're younger, chances I've read you are negligible. (But if you're a dead past master, chances are I've read most of your work.)

     There's a large crop of science fiction writers, famous in the last twenty years, whose work I detest when I'm not simply indifferent.

     A sub genre in sci-fi called speculative fiction started in, say, the 1970's (or there about). It seemed to revel in topics taboo in earlier periods. For example, edgy sexual content was typical. I hate the stuff. It all strikes me as random bullshit: mediocre literary wanking on tour in science fiction slums.

     (The stuff I write isn't up to my standards for high quality work, but it suits my tolerance for speed plots in pulp fiction. So I can bear my own writing.)

     There's another sub genre called (something like) hard science fiction, focusing on science-y themes like space exploration. I like the structure, but I hate the boring material. Usually stories are about nothing whatsoever except soap opera grade planet tourism, like an upscale travel guide.

     Here's the formula I like: go totally nuts, but when it's not possible, try to apologize in good style. You can put impossible things in stories as long as you hang "suspend disbelief" signs in plain sight, and you pay me off for believing, with a plot twist requiring your indiscretion with reality. You get no points for strange ideas unless you make them yield something: prove it was necessary for your plot.

     In other words, if you celebrate absence of rules and do anything you like without structure—a literary free-for-all food fight—then your writing is crap. But if you try to make something as consistent as you can after assuming weird presuppositions, that's more interesting. The weirdness I like, but you have to argue long and hard it's not weird, that you're totally serious. It's like humor: funny material requires a straight face. If you laugh during farce, it's not funny. Weird premises are only good when you tackle them with a straight face, and really dig in. Hyberbole in dry tones is fabulous. Playing for yuks is sophomoric.

18apr09 digital mugshots

context

     (See the fiction page first for context.)

title

     Eli left the Doge's Palace and headed back.

     (Continued at fiction.)

ishtar

     Zé froze the video's playback when Red pulled the gun on him because chatter was getting loud. He looked at Finch: she stood with her arms crossed, nursing a sour but thoughtful expression.

     (Continued at fiction.)

tik-tok

     Wil had trouble switching back and forth between his own view and Tok's. Finch said eventually he could see both at once, one with each eye, and his brain would cope. It was hard to believe now. Tok was studded with sensors, so Wil could pan his view—and jump around with random access—without needing any physical movement on Tok's part.

     (Continued at fiction.)

yahtzee

     Wil's glasses drew an outline around Zé's horn, the ring, and the line connecting them. Finch inhaled sharply.

     (Continued at fiction.)

max headroom

     As Finch removed a round dinner plate sized piece of equipment, Zé studied his ring in puzzlement. "Why doesn't it seem to do anything? Where's the interface?" he asked.

     (Continued at fiction.)

16apr09 swords to plowshares

drafting scenes

     One evening this week I spent a couple hours drafting parts of the next scene. This is the entertaining scene I aimed for earlier. But it's actually easier to write parts you think are throw-away context. When you care about details, it's tempting to make basic mistakes. First, you forget to add sub plots alleviating tight focus. Second, you over-sell a bit, which stinks. A story reads better when you care less whether a reader follows a plot; a light touch is better. Some of this is fixed by editing first drafts to under-sell points.

     I've been trying to preserve interpretations. If the story can go one of several ways, I try to support all of them. To a small extent, I'm trying to make them all true, and not mutually exclusive. I have one interpretation in mind, but I have no special need to kill yours off while I'm at it. I rarely use omniscient perspective to say what Finch is doing, since it too easily eliminates options. Almost as bad is having Finch sell a viewpoint too strongly, since this might be taken as shorthand for true.

     However, sooner or later Wil is going to travel in time, for some definition of time suiting the story's model. So Wil vouches for that part, assuming you trust his view. Tripping is obligatory once the setup gets this deep. Once you're able to do something cool, you have to do it. Otherwise it frustrates the whole contract that stories mean anything.

     Obviously the story is about a lot of things, including the nature of reality. It may deal more with that over time, not less, branching to explore construction of social reality. Just because folks around you subscribe to a view doesn't make good evidence that view is fact.

busy

     I'm in getting-stuff-done mode. I'm writing a lot of code for my day job, and after dinner, late at night, I'm hacking parts of my library into a BSD licensed bundle just so I can clone it into test systems at work next week, for use in some prototypes I'm trying to crank out in a hurry.

     So this weekend I'll post around five thousand lines of interesting code similar to many of the demos, but with a BSD license. I said some time ago I'd rework the code to avoid symbols starting with lowercase y, to avoid my other license. That's what I'm doing now.

     Except I'm hacking out pieces I don't think I'll need, which don't have downstream dependencies. Mostly what I'm doing is giving class names slightly meaningful names, as a halfway step toward code acceptable at work. So for example, the thorn class names yd, ydz and ydp become Deck, DeckCut and DeckIter respectively, under the new cy namespace. It's a lot of careful global symbol replacement.

     I no longer think I'm making any sort of finished product out of my thorn library. So pragmatic hacks like this might be my only code output here for quite some time. The result may be ugly at times. For example, a class will appear more than once, with different names and couplings, just because it was the simplest hack at one moment.

     Instead, I plan to code in order to get specific things done. So how I get a thing done, in pure style points, will have low priority. I'll accept duplication when each new instance has fewer inter-object dependencies in some context. Cherry picking code to get something done is the focus. Some of my efforts might just translate C++ into simpler C when that gets the code used for something.

     The other part of getting things done is fiction. I'm going to keep writing it, though I'm not sure why. When I re-read what I have so far, I like it. I guess that's enough. I'll put my name on it sooner or later. But I don't like being found online, so I might only use first names (the one I go by, and my legal name) without a surname.

13apr09 true names

backfill

     (Here's a new fragment near the story's start. I picked a short name out of a list I liked, and it gave an interesting air to Zé. But I never clarified a bit of cultural antagonism with Ulf. Because Zé prefers to ignore it.)

nickname

     "Is Zé really your name?" Ulf asked Zé.

     Zé looked up at Ulf, squinting in speculation. "You're about to tread on dangerous ground," he said. "You can call me Z, Zé, or Zeta—even Mr. Thorn, though that sounds like my father."

     "But what is your name actually?" Ulf persisted.

     "I know I seem like an affable person," Zé smiled with a hint of whimsy. "It might give you an idea it's okay to play power games, starting with my name. But on this project you work for me, remember?"

     "Isn't Zé short for something else?" Ulf asked. "Do other folks from Brazil have that name, too?"

     Zé stopped smiling. "In a book of names, you may see Zé listed: a Portugese nickname for José. But if you call me José, you're fired."

     Ulf swallowed but tried to look unruffled.

     "Furthermore," Zé smiled again now, "if I hear you ask someone else about my 'real' name, you're fired. In fact, if I ever hear you allude to a possibility my name might be José, to anyone, you're fired. Is this injunction sufficiently all inclusive to clarify things for you?"

     "Yes," Ulf looked chastened.

     "If you want to get stuffy, I can call you Varg while you call me Thorn," Zé said. "But don't mess with me."

     "No, no, I was out of line," Ulf nodded.

     "I don't look like a guy named José," Zé insisted. "I look more like a McFly, don't you think? Except for skin tone I mean. Tall, skinny, and dark."

     "Your obsession with American popular culture is striking," Ulf noted. "I mean, er, United States."

Entries appear in reverse chronological order. Content here is permanent: Each entry has a permalink () to the long-lived persistent copy here. Clearly, to link anything, you'd best link the permanent copy.

12apr09 tastier tomatoes

flavor

     Today I saw a "tastier tomato" reference. It reminded me of a joke coming soon: Zé goes on a dream quest (it seems more like a dream than not). He encounters Poe and Vex in a stolen time machine in the form of a car: an old-school gas guzzling red convertible. What's their mission? They're going back to the 1960's to find tasty tomatoes. It seems they can't find a higher priority use for a time machine.

     This joke has been on the way about five years now—which is one of the reasons I'm squeezing it in.

event simulation

     Below I present some code I plan to use for event simulation. I need something like this at work, and it seems trivial to me, but last week my manager pulled me aside and asked "have you written an event simulator before?" and then looked at me with incredulity, no matter what I said. I understand someone at work wants to download an open source event simulator and go to town. How about this one? All the code below using a cy_ prefix has a BSD license. Normally I'd never open source anything with any relation to a work task. But in this case, it was made clear to me no work task would arise related to event simulation, because it sounds like a waste of my time. For some reason, my time is hoarded like something too valuable to waste. (Because it "sounds" complex, folks assume something complex is involved. But that's not so. Tell me if you think this code is hard.)

     I wrote all this today (Sunday afternoon) instead of writing more fiction. It was also kinda fun, so I don't miss writing the fiction, which can wait.

     However, before the code, first a little introduction. I wrote a discrete event simulator in college because a professor asked for one. It wasn't a class assignment. I was one of the few students she could ask for bespoke code and expect to get something useful. This was a long time ago, but still during a period in which my memory was excellent. (I still recall most things from that period just as clearly—I think.)

     To model virtual time is surprisingly easy. All you need to do is queue up events to be delivered, then deliver a next event whose time of delivery is earliest. When this event is delivered, you set your virtual clock to use the time of this event. All I can say about this is: it was obvious to me. (Lots of things are.)

     The code below is designed around the idea just keeping a priority queue in the form of a binary heap is sufficient. I poked around in Wikipedia for references and add these links to a comment, so folks reading the code later can go find a clue:

/* SEE ALSO: * http://en.wikipedia.org/wiki/Heapsort * http://en.wikipedia.org/wiki/Binary_heap * http://en.wikipedia.org/wiki/Heap_(data_structure) * http://en.wikipedia.org/wiki/Heapsort * http://en.wikipedia.org/wiki/Priority_queue * http://en.wikipedia.org/wiki/Discrete_event_simulation */

     All this code is in C, or something very near C, because some clients will be anti C++. If I write it in C++, there's too big a chance it will be spat upon. I don't enjoy writing C as much as C++, but I no longer particularly notice any more when C is my only choice, because I transliterate C++ to C as I write the code, hand-rolling vtables as needed. I understand this kind of C code disturbs a few folks. (Too bad.)

     Here's a standard macro to inform the compiler a branch is not expected to be taken:

#define cy_unlikely(x) __builtin_expect((x)!=0,0)

     And here's one of my zillions of hand-rolled pseudo random number generators. (This one comes from the hash demo.)

static inline unsigned long cy_u64hash32(unsigned long long n) { /* note: n is 64-bit ONLY so n * 48271 cannot */ /* overflow 32 bits; note: largest prime in */ /* 32-bits: 4294967291 == 0xfffffffb. */ return (unsigned long) ((n * 48271) % 4294967291ULL); };

     Later stages of simulation will use floating point guassian distributions based on the Box Muller transformation shown in the rand demo, but that's not used in code below. However, it should be obvious that pseudo random time delays will involve a gaussian means and standard deviations. (I guess I first learned about use of gaussian distributions in discrete event simulation when I wrote the code in school many years ago.)

     Folks make it hard to use standard iovecs, so:

typedef struct cy_iovec_ { /* clone of iovec */ void* iov_base; size_t iov_len; } cy_iovec_t;

     Below is an event to put in priority queues. It's small because it assumes we need nothing more than virtual time of delivery and an object. The "object" part is code (function pointer) and state (opaque data context). These are values meant to be copied in and out.

typedef void (*cy_event_cb)(void* cx, uint64_t when); typedef struct cy_event_ { /* function vtable */ uint64_t ev_when; /* virtual microseconds */ cy_event_cb ev_cb; /* event callback */ void* ev_cx; /* event context */ } cy_event; static inline void /* convenience inline method */ cy_event_cb_do(const cy_event* e) { /* run callback */ if (e->ev_cb) { (*e->ev_cb)(e->ev_cx, e->ev_when); } }

     The binary min heap used as a priority queue below is slightly more complex because it has "virtual" methods for memory allocation so the event array can be dynamically resized if you provide function pointers to alloc and free memory. (However, note I haven't tested the reserve() method even though I wrote it carefully. I only used statically allocated memory in testing.)

typedef struct cy_bheap_ cy_bheap; /* forward for methods */ typedef void* (*cy_bh_malloc_fn)(cy_bheap* h, size_t size); typedef void (*cy_bh_free_fn)(cy_bheap* h, void *ptr);

     Those are malloc() and free() virtual methods.

/* index zero is never used in this binary heap formulation: */ #define cy_bheap_ROOT 1 /* root member always at index ONE */

     I use the binary heap definition ignoring the zero array element, so a left child and right child are defined as follows:

/* arg i MUST NOT BE ZERO in left() and right(): */ static inline unsigned /* index of left child of i */ cy_bheap_left(unsigned i) { return 2 * i; } static inline unsigned /* index of right child of i */ cy_bheap_right(unsigned i) { return (2 * i)+1; }

     At the root (least) member is always at index 1:

/* index zero is never used in this binary heap formulation: */ #define cy_bheap_ROOT 1 /* root member always at index ONE */

     Here's a few more useful inlines related to root and children. (Sometimes I don't bother using these inlines because they obfuscate code. But I like having them around to define rules clearly.)

static inline int /* left child of j, or -1 if it's missing */ cy_bheap_lch(const cy_bheap* h, unsigned j) { unsigned l = 2 * j; if (l <= h->bh_size) return (int) l; /* left is in bounds with a context */ else return -1; } static inline int /* right child of j, or -1 if it's missing */ cy_bheap_rch(const cy_bheap* h, unsigned j) { unsigned r = (2 * j)+1; if (r <= h->bh_size) return (int) r; /* right is in bounds with a context */ else return -1; } static inline unsigned /* index of parent for child at i */ cy_bheap_parent(unsigned i) { return i / 2; } extern cy_event cy_event_oob; /* out-of-bounds default event */ static inline cy_event /* least event now in heap */ cy_bheap_root(const cy_bheap* h, unsigned i) { if (h->bh_events && h->bh_size) { /* not empty? */ return h->bh_events[cy_bheap_ROOT]; } return cy_event_oob; /* default for out-of-bounds */ }

     The binary heap object looks like this:

#define cy_bheap_TAG 0x42684550 /*'BhEP'*/ struct cy_bheap_ { /* binary 'heap' (for priority queue)*/ /* bh_now tracks the usecs in each least event removed: */ uint64_t bh_now; /* virtual clock for this heap*/ cy_bh_malloc_fn bh_malloc; /* malloc() */ cy_bh_free_fn bh_free; /* free() */ void* bh_cx; /* if malloc() and free() need */ uint32_t bh_tag; /* must equal TAG */ cy_event* bh_events; /* array of events */ /* size is the index of the last USED array slot: */ uint32_t bh_size; /* count of USED events */ /* REQUIRED: capacity >= size+1 (zero index is unused) */ uint32_t bh_capacity; /* physical array length */ };

     Whose constructor and destructor are:

/*you can either alloc all events up front, or dynamic alloc: */ static inline void cy_bheap_ctor(cy_bheap* h, cy_event* v, unsigned capacity) { h->bh_now = 0; /* beginning of time: zero microseconds */ h->bh_malloc = 0; /* replace if you need auto-grow */ h->bh_free = 0; /* replace if you need auto-grow */ h->bh_cx = 0; /* replace if methods above need a context */ h->bh_tag = cy_bheap_TAG; /* magic signature */ h->bh_events = v; /* vector of starting empty space*/ h->bh_size = 0; /* no members in heap at first */ h->bh_capacity = (v)? capacity : 0; /*starting events (if any)*/ } static inline void cy_bheap_dtor(cy_bheap* h) { void* events = h->bh_events; cy_bh_free_fn fn = h->bh_free; /* nil if not needed */ if (cy_bheap_TAG == h->bh_tag) { /* heap seems valid? */ h->bh_tag = 0xdeadbeef; if (fn) { /* we have a method to deallocate events? */ h->bh_events = 0; /* never do this again */ h->bh_free = 0; (*fn)(h, events); /* free array space if desired */ } } else { /* remove this block if you want no err checking */ cy_printf("bh_tag=0x%lx not TAG=0x%lx", (long) h->bh_tag, (long) cy_bheap_TAG); assert(cy_bheap_TAG == h->bh_tag); /* die */ } }

bheap api

     The primary api looks like this:

#ifdef cy_bheap_PRINTING void cy_bheap_print(const cy_bheap* h, cy_sink* s); #endif /*cy_bheap_PRINTING*/ void cy_bheap_test(uint32_t seed); /* needs printing code */ /* reserve() tries to make sure capacity is at least min: */ int /* zero: success; nonzero: errno (probably no memory) */ cy_bheap_reserve(cy_bheap* h, unsigned minCapacity); /* insert() appends event, then bubbles upward */ int /* zero: success; nonzero: errno (probably no memory) */ cy_bheap_insert(cy_bheap* h, cy_event e); /* add copy of e */ /* append() is more efficient than N adds for large N */ int /* zero: success; nonzero: errno (probably no memory) */ cy_bheap_append(cy_bheap* h, const cy_event* v, unsigned len); /* side effect of remove(): bh_now = max(bh_now, ev_when) */ cy_event /* the root of the tree, now removed from the heap*/ cy_bheap_remove(cy_bheap* h); /* event with least ev_when*/ /* delay means: insert(h, cy_event(h->bh_now+usecs, cb, cx)) */ int /* queue future event at now + usecs (delayed by usecs) */ cy_bheap_delay(cy_bheap* h, unsigned usecs, cy_event_cb cb, void* cx) { cy_event e; e.ev_when = h->bh_now + usecs; e.ev_cb = cb; e.ev_cx = cx; return cy_bheap_insert(h, e); }

bheap algorithm

     I added a helpful note on algorithms:

/* BASIC ALGORITHMS: remove and insert, as follows: * REMOVE: * cy_event outputEv = bh_events[cy_bheap_ROOT]; * unsigned i = bh_size--; * bh_events[cy_bheap_ROOT] = bh_events[i]; // can't be too low * cy_bheap_siftdown(this, 1); // mv down while any child is less * INSERT: * cy_bheap_reserve(bh_size+1) // ensure room exists * unsigned i = ++bh_size; // new last event in array * bh_events[i] = inputEv; // can't be too hi starting at bottom * cy_bheap_siftup(this, i); // mv child up while parent is more */

bheap reserve

     Here's how you reserve capacity. Note this hasn't been tested or even run. If it looks debugged, that just reflects how I think carefully. Yes, I know this sort of thing is hard for some folks. But it's old hat to me.

/* reserve() tries to make sure capacity is at least min: */ int /* zero: success; nonzero: errno (probably no memory) */ cy_bheap_reserve(cy_bheap* h, unsigned minCapacity) { assert(cy_bheap_TAG == h->bh_tag); if (minCapacity > 1024*1024) /* over a million?*/ minCapacity = 1024*1024; /* that's enough */ if (h->bh_capacity < minCapacity) { /* need more? */ cy_bh_malloc_fn fn = h->bh_malloc; if (fn) { /* we can alloc memory? */ cy_event* old = h->bh_events; /* old content */ unsigned have = h->bh_size + 1; /* unused zero */ unsigned want = minCapacity + 1024; /* more */ unsigned cmin = 4 * (h->bh_capacity/3); /* + 1/3 */ if (want < cmin) /* less than one third increase? */ want = cmin; /* geometric growth when bigger */ unsigned newvol = sizeof(cy_event) * want; /* bytes */ cy_event* e = (cy_event*) (*fn)(h, newvol); /* alloc */ if (e) { /* good allocation? */ if (old) { /* anything to copy or free? */ uint8_t* p = (uint8_t*) e; /* byte pointer */ unsigned oldvol = have * sizeof(cy_event); memcpy(p, old, oldvol); /* copy old events */ unsigned extra = newvol - oldvol; /* bytes */ memset(p + oldvol, 0, extra); /* zero new */ assert(0 != h->bh_free); /* required */ (*h->bh_free)(h, old); /* reclaim */ } else { /* all zero with nothing to copy */ memset(e, 0, newvol); } h->bh_events = e; h->bh_capacity = want; return 0; /* bigger capacity is good to go */ } } return ENOMEM; /* cannot alloc or failed alloc */ } return 0; /* capacity already big enough */ }

bheap insert

     Below a single new event is inserted.

void /* swap child at j w/ parent that's bigger, then recurse */ cy_bheap_siftup(cy_bheap* h, unsigned j) { cy_event* v = h->bh_events; uint64_t when = v[j].ev_when; /* key to move upward */ while (j > cy_bheap_ROOT) { /* j has a parent? */ unsigned p = j / 2; /* parent */ if (v[p].ev_when > when) { /* bigger parent?*/ cy_event t = v[j]; v[j] = v[p]; v[p] = t; /* swap */ j = p; /* recurse on parent */ } else { /* done when parent is not bigger */ break; } } } /* insert() appends event, then bubbles upward */ int /* zero: success; nonzero: errno (probably no memory) */ cy_bheap_insert(cy_bheap* h, cy_event e) { /* add copy of e */ unsigned i = h->bh_size + 1; /* where to insert at first */ assert(cy_bheap_TAG == h->bh_tag); if (cy_unlikely(i >= h->bh_capacity)) { /* need slots? */ int err = cy_bheap_reserve(h, i + 32); if (err) /* ENOMEM? */ return err; /* cannot insert */ } assert(i < h->bh_capacity); /* must have slot to use */ h->bh_size = i; /* alloc first unused slot */ h->bh_events[i] = e; cy_bheap_siftup(h, i); /* move upward while parent is less */ return 0; }

bheap remove

     Removing the least event (the root) works like this:

void /* swap event at j with smaller child, then recurse */ cy_bheap_siftdown(cy_bheap* h, unsigned j) { cy_event* v = h->bh_events; uint64_t when = v[j].ev_when; /* key to move downward */ unsigned last = h->bh_size; /* last used event */ unsigned parent = last / 2; /* parent of last event */ while (j <= parent) { /* might have children? */ unsigned l = 2 * j; /* left child */ unsigned r = l + 1; /* right child */ unsigned c = l; /* assume left is least child */ if (r <= last && v[r].ev_when < v[c].ev_when) { c = r; /* use r as index of least child */ } if (when > v[c].ev_when) { /* smaller child?*/ cy_event t = v[j]; v[j] = v[c]; v[c] = t; /* swap */ j = c; /* recurse on least child */ } else { /* done when neither child is smaller */ break; } } } /* side effect of remove(): bh_now = max(bh_now, ev_when) */ cy_event /* the root of the tree, now removed from the heap*/ cy_bheap_remove(cy_bheap* h) { /* event with least ev_when*/ cy_event outEv; cy_event* v = h->bh_events; unsigned i = h->bh_size; /* used heap array elems */ if (v && i) { /* have anything to remove? */ assert(cy_bheap_TAG == h->bh_tag); assert(h->bh_capacity > i); /* must exceed size */ outEv = v[cy_bheap_ROOT]; /* least event in heap */ if (h->bh_now < outEv.ev_when) { h->bh_now = outEv.ev_when; } v[cy_bheap_ROOT] = v[i]; /* last event becomes root */ h->bh_size = i - 1; /* heap is one smaller */ cy_bheap_siftdown(h, cy_bheap_ROOT); } else { memset(&outEv, 0, sizeof(cy_event)); /* nothing */ } return outEv; }

bheap append

     You append an array of unordered events like this:

/* append() is more efficient than N adds for large N */ int /* zero: success; nonzero: errno (probably no memory) */ cy_bheap_append(cy_bheap* h, const cy_event* vec, unsigned len) { assert(cy_bheap_TAG == h->bh_tag); unsigned sz = h->bh_size + len; /* resulting size */ if (cy_unlikely(sz >= h->bh_capacity)) { /* need slots? */ int err = cy_bheap_reserve(h, sz + 32); if (err) /* ENOMEM? */ return err; /* cannot insert */ } assert(sz < h->bh_capacity); /* must have slots to use */ cy_event* v = h->bh_events; /* event array (maybe new) */ unsigned addvol = sizeof(cy_event) * len; /* bytes to add */ unsigned prefix = h->bh_size + 1; /* include unused zero */ memcpy(v + prefix, vec, addvol); /* append events */ h->bh_size = sz; /* count all new events as added */ unsigned parent = sz / 2; /* parent of last event */ /* note: all events at indexes > sz / 2 have no children */ for (unsigned i = parent; i >= cy_bheap_ROOT; --i) { cy_bheap_siftdown(h, i); /* down while child is more */ } return 0; }

bheap testing

     Now, you might find part of my test below a little disturbing. I ported my C++ out stream class from the C++ (in the out demo) to C, so I could test the binary heap using a C based stream api.

     I only did this so folks who dislike C++ can print the binary heaps using C only. And perhaps I might find that C stream useful again. However, the C code for the stream is quite long—it took me an hour or two write. I'll show that code last, after test code and resulting output.

void cy_bheap_test(uint32_t seed) { /* needs printing code */ unsigned i = 0; cy_event finiteVec[ 2048 ]; cy_event inboundVec[ 1024 ]; /* for vector append */ cy_event e; cy_bheap minheap; char buf[ 2048 + 4 ]; cy_fdsink sout; cy_iovec_t bufiov; bufiov.iov_base = buf; bufiov.iov_len = 2048; cy_fdsink_ctor(&sout, bufiov, STDOUT_FILENO); cy_bheap_ctor(&minheap, finiteVec, 2048); for ( ; i < 16; ++i) { seed = cy_u64hash32(seed); e.ev_when = seed; cy_bheap_insert(&minheap, e); } cy_bheap_print(&minheap, &sout.f_sink); cy_sink_n(&sout.f_sink); for (i = 0; i < 8; ++i) { seed = cy_u64hash32(seed); inboundVec[i].ev_when = seed; cy_sink_f(&sout.f_sink, " %08lx", seed); } cy_sink_n(&sout.f_sink); cy_bheap_append(&minheap, inboundVec, 8); cy_bheap_print(&minheap, &sout.f_sink); while (minheap.bh_size) { (void) cy_bheap_remove(&minheap); cy_bheap_print(&minheap, &sout.f_sink); } }

     The print method and the fdsink stream api appear in the next section. Here's the output on stdout:

<bheap now=00000000 size=16 cap=2048> 15: cbd0b86a 7: 6d4482df 14: d9193a29 3: 2b29dad9 13: 65fc300d 6: 596270f8 12: f1d9ff88 1: 12d34ae1 11: 523c8b1d 5: 24d57108 10: 307117a7 2: 1c8b8caf 9: 61500c69 4: 2107c9d1 8: 38f483b6 16: af045500 </bheap> 2b8fb163 db896ab6 8c236c29 4b398aff 4738fd79 a2fb78e0 b73f5d57 e4f3aa71 <bheap now=00000000 size=24 cap=2048> 15: cbd0b86a 7: 6d4482df 14: d9193a29 3: 2b29dad9 13: 65fc300d 6: 596270f8 12: e4f3aa71 24: f1d9ff88 1: 12d34ae1 23: b73f5d57 11: 523c8b1d 22: a2fb78e0 5: 24d57108 21: 4738fd79 10: 307117a7 20: 4b398aff 2: 1c8b8caf 19: 8c236c29 9: 61500c69 18: db896ab6 4: 2107c9d1 17: 38f483b6 8: 2b8fb163 16: af045500 </bheap> <bheap now=12d34ae1 size=23 cap=2048> 15: cbd0b86a 7: 6d4482df 14: d9193a29 3: 2b29dad9 13: 65fc300d 6: 596270f8 12: e4f3aa71 1: 1c8b8caf 23: b73f5d57 11: 523c8b1d 22: a2fb78e0 5: 24d57108 21: 4738fd79 10: 307117a7 20: 4b398aff 2: 2107c9d1 19: 8c236c29 9: 61500c69 18: db896ab6 4: 2b8fb163 17: f1d9ff88 8: 38f483b6 16: af045500 </bheap> <bheap now=1c8b8caf size=22 cap=2048> 15: cbd0b86a 7: 6d4482df 14: d9193a29 3: 2b29dad9 13: 65fc300d 6: 596270f8 12: e4f3aa71 1: 2107c9d1 11: 523c8b1d 22: a2fb78e0 5: 307117a7 21: b73f5d57 10: 4738fd79 20: 4b398aff 2: 24d57108 19: 8c236c29 9: 61500c69 18: db896ab6 4: 2b8fb163 17: f1d9ff88 8: 38f483b6 16: af045500 </bheap> <bheap now=2107c9d1 size=21 cap=2048> 15: cbd0b86a 7: 6d4482df 14: d9193a29 3: 2b29dad9 13: 65fc300d 6: 596270f8 12: e4f3aa71 1: 24d57108 11: 523c8b1d 5: 307117a7 21: b73f5d57 10: 4738fd79 20: 4b398aff 2: 2b8fb163 19: 8c236c29 9: 61500c69 18: db896ab6 4: 38f483b6 17: f1d9ff88 8: a2fb78e0 16: af045500 </bheap> <bheap now=24d57108 size=20 cap=2048> 15: cbd0b86a 7: 6d4482df 14: d9193a29 3: 596270f8 13: b73f5d57 6: 65fc300d 12: e4f3aa71 1: 2b29dad9 11: 523c8b1d 5: 307117a7 10: 4738fd79 20: 4b398aff 2: 2b8fb163 19: 8c236c29 9: 61500c69 18: db896ab6 4: 38f483b6 17: f1d9ff88 8: a2fb78e0 16: af045500 </bheap> <bheap now=2b29dad9 size=19 cap=2048> 15: cbd0b86a 7: 6d4482df 14: d9193a29 3: 596270f8 13: b73f5d57 6: 65fc300d 12: e4f3aa71 1: 2b8fb163 11: 523c8b1d 5: 4738fd79 10: 4b398aff 2: 307117a7 19: 8c236c29 9: 61500c69 18: db896ab6 4: 38f483b6 17: f1d9ff88 8: a2fb78e0 16: af045500 </bheap> <bheap now=2b8fb163 size=18 cap=2048> 15: cbd0b86a 7: 6d4482df 14: d9193a29 3: 596270f8 13: b73f5d57 6: 65fc300d 12: e4f3aa71 1: 307117a7 11: 523c8b1d 5: 4738fd79 10: 4b398aff 2: 38f483b6 9: 8c236c29 18: db896ab6 4: 61500c69 17: f1d9ff88 8: a2fb78e0 16: af045500 </bheap> <bheap now=307117a7 size=17 cap=2048> 15: cbd0b86a 7: 6d4482df 14: d9193a29 3: 596270f8 13: b73f5d57 6: 65fc300d 12: e4f3aa71 1: 38f483b6 11: 523c8b1d 5: 4b398aff 10: db896ab6 2: 4738fd79 9: 8c236c29 4: 61500c69 17: f1d9ff88 8: a2fb78e0 16: af045500 </bheap> <bheap now=38f483b6 size=16 cap=2048> 15: cbd0b86a 7: 6d4482df 14: d9193a29 3: 596270f8 13: b73f5d57 6: 65fc300d 12: e4f3aa71 1: 4738fd79 11: f1d9ff88 5: 523c8b1d 10: db896ab6 2: 4b398aff 9: 8c236c29 4: 61500c69 8: a2fb78e0 16: af045500 </bheap> <bheap now=4738fd79 size=15 cap=2048> 15: cbd0b86a 7: 6d4482df 14: d9193a29 3: 596270f8 13: b73f5d57 6: 65fc300d 12: e4f3aa71 1: 4b398aff 11: f1d9ff88 5: af045500 10: db896ab6 2: 523c8b1d 9: 8c236c29 4: 61500c69 8: a2fb78e0 </bheap> <bheap now=4b398aff size=14 cap=2048> 7: 6d4482df 14: d9193a29 3: 596270f8 13: b73f5d57 6: 65fc300d 12: e4f3aa71 1: 523c8b1d 11: f1d9ff88 5: af045500 10: db896ab6 2: 61500c69 9: cbd0b86a 4: 8c236c29 8: a2fb78e0 </bheap> <bheap now=523c8b1d size=13 cap=2048> 7: 6d4482df 3: 65fc300d 13: d9193a29 6: b73f5d57 12: e4f3aa71 1: 596270f8 11: f1d9ff88 5: af045500 10: db896ab6 2: 61500c69 9: cbd0b86a 4: 8c236c29 8: a2fb78e0 </bheap> <bheap now=596270f8 size=12 cap=2048> 7: 6d4482df 3: 65fc300d 6: b73f5d57 12: e4f3aa71 1: 61500c69 11: f1d9ff88 5: af045500 10: db896ab6 2: 8c236c29 9: cbd0b86a 4: a2fb78e0 8: d9193a29 </bheap> <bheap now=61500c69 size=11 cap=2048> 7: e4f3aa71 3: 6d4482df 6: b73f5d57 1: 65fc300d 11: f1d9ff88 5: af045500 10: db896ab6 2: 8c236c29 9: cbd0b86a 4: a2fb78e0 8: d9193a29 </bheap> <bheap now=65fc300d size=10 cap=2048> 7: e4f3aa71 3: b73f5d57 6: f1d9ff88 1: 6d4482df 5: af045500 10: db896ab6 2: 8c236c29 9: cbd0b86a 4: a2fb78e0 8: d9193a29 </bheap> <bheap now=6d4482df size=9 cap=2048> 7: e4f3aa71 3: b73f5d57 6: f1d9ff88 1: 8c236c29 5: af045500 2: a2fb78e0 9: db896ab6 4: cbd0b86a 8: d9193a29 </bheap> <bheap now=8c236c29 size=8 cap=2048> 7: e4f3aa71 3: b73f5d57 6: f1d9ff88 1: a2fb78e0 5: db896ab6 2: af045500 4: cbd0b86a 8: d9193a29 </bheap>

bheap print

     This printing code is recursive. (Duh.) If you don't like the stream api, too bad. Note code detecting inverted time order between parents and children. It happened and I needed to debug it, so I added this code.

#ifdef cy_bheap_PRINTING void cy_bheap_show(const cy_bheap* h, cy_sink* s, int j) { int inverted = (j < 0); /* negative index? */ if (inverted) /* note inverted order with parent */ j = -j; /* only use positive indexes */ uint64_t here = h->bh_events[j].ev_when; int r = cy_bheap_rch(h, j); /* right: -1 if missing */ if (r > 0) { /* has a right child too? */ if (here > h->bh_events[r].ev_when) { /* inverted?? */ r = -r; /* show inversion */ assert(here <= h->bh_events[r].ev_when); /* die */ } cy_sink_t(s); /* tab then newline indent */ cy_bheap_show(h, s, r); cy_sink_u(s); /* untab */ } if (inverted) /* show inversion of order with parent: */ cy_sink_nf(s, "%u @@ %08llx ", j, here); else cy_sink_nf(s, "%u: %08llx ", j, here); int l = cy_bheap_lch(h, j); /* left: -1 if missing */ if (l > 0) { /* has a left child? */ if (here > h->bh_events[l].ev_when) { /* inverted?? */ l = -l; /* show inversion */ assert(here <= h->bh_events[l].ev_when); /* die */ } cy_sink_t(s); /* tab then newline indent */ cy_bheap_show(h, s, l); cy_sink_u(s); /* untab */ } } void cy_bheap_print(const cy_bheap* h, cy_sink* s) { cy_sink_f(s, "<bheap now=%08llx size=%d cap=%d>", (long long) h->bh_now, (int) h->bh_size, (int) h->bh_capacity); if (h->bh_size) { cy_bheap_show(h, s, cy_bheap_ROOT); } cy_sink_nf(s, "</bheap>"); cy_sink_n(s); sink_flush_do(s); } #endif /*cy_bheap_PRINTING*/

stream header

     I won't say much about the C stream api at all. Read the out demo for explanations.

typedef struct cy_sink_ cy_sink; /* output sink stream */ typedef int /* like ::write() */ (*cy_sink_write_fn)(cy_sink* s, const void* b, uint32_t n); typedef int (*cy_sink_flush_fn)(cy_sink* s); typedef void (*cy_sink_putc_fn)(cy_sink* s, int c); /* fallback */ struct cy_sink_ { /* 'sink' output stream */ cy_sink_write_fn s_write; cy_sink_flush_fn s_flush; cy_sink_putc_fn s_putc; uint8_t* s_0; /* origin (start of buffer) */ uint8_t* s_p; /* pointer (cursor in buffer) */ uint8_t* s_x; /* extent (one past last buf byte) */ int s_err; /* zero or error */ uint16_t s_tab; /* current indent depth */ uint16_t s_pad; /* reserved (alignment) */ }; static inline void cy_sink_ctor(cy_sink* s, cy_sink_write_fn w, cy_sink_flush_fn f, cy_sink_putc_fn c, cy_iovec_t v) { uint8_t* p = (uint8_t*) v.iov_base; s->s_write = w; s->s_flush = f; s->s_putc = c; if (p && v.iov_len) { /* initial buffer exists? */ s->s_0 = s->s_p = p; s->s_x = p + v.iov_len; /* one past last buf byte */ } else { s->s_0 = s->s_p = s->s_x = 0; /* empty buffer */ } s->s_err = 0; s->s_tab = 0; } /* writev() writes each iovec to sink s using sink_write_do() */ int /* -1 on error; otherwise sum of bytes written */ cy_sink_writev(cy_sink* s, const cy_iovec_t* v, int cnt); static inline int /* virtual dispatch */ sink_write_do(cy_sink* s, const void* b, uint32_t n) { return (*s->s_write)(s, b, n); } static inline int /* virtual dispatch */ sink_flush_do(cy_sink* s) { return (*s->s_flush)(s); } static inline void /* sometimes virtual dispatch if needed */ cy_sink_c(cy_sink* s, int c) { /* write one byte */ /* note: virtual putc() only called when buffer is full: */ if (s->s_p < s->s_x) { /* room for a byte in buf? */ *s->s_p++ = (uint8_t) c; } else { (*s->s_putc)(s, c); /* flush and then handle byte */ } } void cy_sink_1c(cy_sink* s, int c); /* non-inline c() above*/ void cy_sink_2c(cy_sink* s, int a, int b); /* c(a); c(b); */ void cy_sink_3c(cy_sink* s, int a, int b, int c); static inline void cy_sink_line(cy_sink* s) { cy_sink_c(s, '\n'); } /* newline only */ void cy_sink_n(cy_sink* s); /* newline then INDENT to depth */ void cy_sink_tabs(cy_sink* s, unsigned count); /* count tabs */ static inline void /* increase tab depth by one */ cy_sink_t(cy_sink* s) { ++s->s_tab; } static inline void /* increase tab depth by two */ cy_sink_tt(cy_sink* s) { s->s_tab += 2; } static inline void /* untab depth by one */ cy_sink_u(cy_sink* s) { if (s->s_tab) --s->s_tab; } static inline void /* untab depth by two */ cy_sink_uu(cy_sink* s) { if (s->s_tab >= 2) s->s_tab -= 2; } static inline void /* tab, then newline indent */ cy_sink_tn(cy_sink* s) { cy_sink_t(s); cy_sink_n(s); } static inline void /* tab twice, then newline indent */ cy_sink_ttn(cy_sink* s) { cy_sink_tt(s); cy_sink_n(s); } static inline void /* untab, then newline indent */ cy_sink_un(cy_sink* s) { cy_sink_u(s); cy_sink_n(s); } static inline void /* untab twice, then newline indent */ cy_sink_uun(cy_sink* s) { cy_sink_uu(s); cy_sink_n(s); } static inline void /* virtual dispatch */ cy_sink_s(cy_sink* s, const char* cstr) { /* write C string */ (void) (*s->s_write)(s, cstr, strlen(cstr)); } static inline void /* tab, then newline indent */ cy_sink_ns(cy_sink* s, const char* cstr) { cy_sink_n(s); cy_sink_s(s, cstr); } static inline void /* tab, then newline indent */ cy_sink_sn(cy_sink* s, const char* cstr) { cy_sink_s(s, cstr); cy_sink_n(s); } static inline void /* tab, then newline indent */ cy_sink_nst(cy_sink* s, const char* cstr) { cy_sink_n(s); cy_sink_s(s, cstr); cy_sink_t(s); } static inline void /* tab, then newline indent */ cy_sink_tns(cy_sink* s, const char* cstr) { cy_sink_t(s); cy_sink_n(s); cy_sink_s(s, cstr); } static inline void /* tab, then newline indent */ cy_sink_st(cy_sink* s, const char* cstr) { cy_sink_s(s, cstr); cy_sink_t(s); } static inline void /* tab, then newline indent */ cy_sink_us(cy_sink* s, const char* cstr) { cy_sink_u(s); cy_sink_s(s, cstr); } static inline void /* tab, then newline indent */ cy_sink_uns(cy_sink* s, const char* cstr) { cy_sink_u(s); cy_sink_n(s); cy_sink_s(s, cstr); } void /* basically sink_2c('<', '/'); sink_s(tag); sink_c('>'); */ cy_sink_end(cy_sink* s, const char* tag); /* '<' '/' tag '>' */ static inline void /* untab, then '<' '/' tag '>' */ cy_sink_nend(cy_sink* s, const char* tag) { cy_sink_n(s); cy_sink_end(s, tag); } static inline void /* untab, then '<' '/' tag '>' */ cy_sink_uend(cy_sink* s, const char* tag) { cy_sink_u(s); cy_sink_end(s, tag); } static inline void /* untab, LF indent, then '<' '/' tag '>' */ cy_sink_unend(cy_sink* s, const char* tag) { cy_sink_un(s); cy_sink_end(s, tag); } static inline void /* untab, LF indent, then '<' '/' tag '>' */ cy_sink_uunend(cy_sink* s, const char* tag) { cy_sink_uun(s); cy_sink_end(s, tag); } static inline void cy_sink_tc(cy_sink* s, int c) { cy_sink_t(s); cy_sink_1c(s, c); } static inline void cy_sink_uc(cy_sink* s, int c) { cy_sink_u(s); cy_sink_1c(s, c); } static inline void cy_sink_ct(cy_sink* s, int c) { cy_sink_1c(s, c); cy_sink_t(s); } static inline void cy_sink_cu(cy_sink* s, int c) { cy_sink_1c(s, c); cy_sink_u(s); } static inline void cy_sink_cn(cy_sink* s, int c) { cy_sink_1c(s, c); cy_sink_n(s); } static inline void cy_sink_ctn(cy_sink* s, int c) { cy_sink_1c(s, c); cy_sink_t(s); cy_sink_n(s); } static inline void cy_sink_cttn(cy_sink* s, int c) { cy_sink_1c(s, c); cy_sink_tt(s); cy_sink_n(s); } void /* f() is basically the same as printf() */ cy_sink_f(cy_sink* s, const char* fmt, ...); /* f() */ void cy_sink_fn(cy_sink* s, const char* fmt, ...); void cy_sink_ft(cy_sink* s, const char* fmt, ...); void cy_sink_ftn(cy_sink* s, const char* fmt, ...); void cy_sink_nf(cy_sink* s, const char* fmt, ...); void cy_sink_nft(cy_sink* s, const char* fmt, ...); /* ----- fdsink ----- */ typedef struct cy_fdsink_ cy_fdsink; /* file descriptor sink */ int cy_fdsink_write(cy_sink* s, const void* b, uint32_t n); int cy_fdsink_flush(cy_sink* s); void cy_fdsink_putc(cy_sink* s, int c); #define cy_fdsink_TAG 0x6664736b /*'fdsk'*/ struct cy_fdsink_ { /* file descriptor 'sink' output stream */ cy_sink f_sink; /* base class */ cy_iovec_t f_iov; /* entire local buffer */ uint32_t f_tag; /* must be TAG */ int f_fd; /* file descriptor (where to write) */ uint32_t f_out; /* bytes written to descriptor */ }; static inline void cy_fdsink_ctor(cy_fdsink* s, cy_iovec_t v, int fd) { cy_sink* base = &s->f_sink; cy_sink_ctor(base, cy_fdsink_write, cy_fdsink_flush, cy_fdsink_putc, v); s->f_iov = v; s->f_tag = cy_fdsink_TAG; s->f_fd = fd; /* change to later if you have new one */ s->f_out = 0; }

stream code

     Finally, here's what you put in the implementation:

static int /* internal/private write to descriptor */ _fdsink_write(cy_fdsink* s, const void* buf, unsigned len) { const uint8_t* src = (const uint8_t*) buf; int tries = 0; int err = 0; unsigned outSize = 0; while (len) { if (++tries > 20) { cy_printf("write() tries=%d errno=%d", tries, err); s->f_sink.s_err = err; return outSize; } int did = ::write(s->f_fd, src, len); if (did < 0) { err = errno; if (EAGAIN == err || EINTR == err) continue; /* try again */ cy_printf("write(m_fd=%d, len=%d)=%d errno=%d", (int) s->f_fd, (int) len, did, err); s->f_sink.s_err = err; return -1; } if (did > (int) len) { cy_printf("write(n=%d) actual=%d??", (int) len, did); did = len; /* do not subtract more then len */ } src += did; len -= did; outSize += did; s->f_out += did; } return outSize; } int /* -1 on error; otherwise sum of bytes written */ cy_fdsink_write(cy_sink* s, const void* buf, uint32_t sz) { unsigned outSize = 0; int actual = 0; cy_fdsink* f = (cy_fdsink*) s; assert(cy_fdsink_TAG == f->f_tag); const uint8_t* src = (const uint8_t*) buf; if (!sz) { return 0; } /* write nothing? */ if (!src) { s->s_err = EINVAL; return -1; } if (cy_unlikely(s->s_p < s->s_0 || s->s_p > s->s_x)) { s->s_err = EINVAL; assert(s->s_p >= s->s_0 && s->s_p <= s->s_x); /* die */ } unsigned room = s->s_x - s->s_p; /* space left in buf */ if (s->s_p == s->s_0) { /* empty? NOTHING in local buf? */ if (sz > f->f_iov.iov_len) { /* cannot fit in buf? */ actual = _fdsink_write(f, src, sz); /* direct */ if (actual < 0) return -1; outSize += (unsigned) actual; } else { /* sz <= iov_len implies it fits in buffer */ memcpy(s->s_p, src, sz); /* safely put in buffer */ s->s_p += sz; outSize += sz; /* advance */ assert(s->s_p <= s->s_x); /* not beyond buf end */ } } else { /* SOME already buffered bytes are present */ if (sz > room) { /* more than fits in space left? */ unsigned more = sz - room; /* excess bytes */ if (room) { /* any more space left at all? */ memcpy(s->s_p, src, room); /* fill, then flush */ s->s_p += room; outSize += room; src += room; } actual = _fdsink_write(f, s->s_0, s->s_p - s->s_0); if (actual < 0) return -1; s->s_p = s->s_0; /* buffer is empty again */ if (sz > f->f_iov.iov_len) { /* cannot fit in buf? */ actual = _fdsink_write(f, src, more); /* direct */ if (actual < 0) return -1; outSize += (unsigned) actual; } else { /* buffer is flushed: remainder now fits */ memcpy(s->s_p, src, more); s->s_p += more; outSize += more; src += more; assert(s->s_p <= s->s_x); /* not beyond buf end */ } } else { /* room >= sz: input bytes fit in space left: */ memcpy(s->s_p, src, sz); s->s_p += sz; outSize += sz; } } return outSize; } int cy_fdsink_flush(cy_sink* s) { cy_fdsink* f = (cy_fdsink*) s; assert(cy_fdsink_TAG == f->f_tag); if (cy_unlikely(s->s_p < s->s_0)) { assert(s->s_p >= s->s_0); /* die */ s->s_err = EINVAL; return EINVAL; } unsigned size = s->s_p - s->s_0; if (size) { /* anything in buffer? */ if (f->f_fd >= 0) { /* descriptor is valid */ s->s_p = s->s_0; /* empty again: origin */ int actual = _fdsink_write(f, s->s_0, size); if (actual < 0) { return actual; } } } return 0; } void cy_fdsink_putc(cy_sink* s, int c) { if (0 == cy_fdsink_flush(s)) { if (s->s_p < s->s_x) { *s->s_p++ = (uint8_t) c; } } } /* ----- sink ----- */ /* writev() writes each iovec to sink s using sink_write_do() */ int /* -1 on error; otherwise sum of bytes written */ cy_sink_writev(cy_sink* s, cy_iovec_t* iov, int cnt) { int sum = 0; for (int i = 0; i < cnt; ++i) { const cy_iovec_t* v = iov + i; int actual = sink_write_do(s, v->iov_base, v->iov_len); if (actual >= 0) sum += actual; else return actual; /* stop on negative */ } return sum; } void cy_sink_1c(cy_sink* s, int c) { /* non-inline c() */ cy_sink_c(s, c); /* inline */ } void cy_sink_2c(cy_sink* s, int a, int b) { /* c(a); c(b); */ cy_sink_c(s, a); /* inline */ cy_sink_c(s, b); /* inline */ } void cy_sink_3c(cy_sink* s, int a, int b, int c) { cy_sink_c(s, a); /* inline */ cy_sink_c(s, b); /* inline */ cy_sink_c(s, c); /* inline */ } void cy_sink_n(cy_sink* s) { /* newline then INDENT to depth */ cy_sink_c(s, '\n'); /* newline */ cy_sink_tabs(s, s->s_tab); /* indent */ } void /* basically sink_2c('<', '/'); sink_s(tag); sink_c('>'); */ cy_sink_end(cy_sink* s, const char* tag) { /* '<' '/' tag '>' */ cy_sink_c(s, '<'); /* inline */ cy_sink_c(s, '/'); /* inline */ sink_write_do(s, tag, strlen(tag)); cy_sink_c(s, '>'); /* inline */ }

static const char* cy_blanks = /* ALL BLANKS on next line with comment ruler: */ " "; /* 123456789_123456789_123456789_123456789_123456789_123456789_123456789_12 */

#define cy_blanks_len 72 /*length of cy_blanks above*/ void cy_sink_tabs(cy_sink* s, unsigned tab) { unsigned sz = tab * 2; if (sz > 1024) /* too big? */ sz = 1024; while (sz) { unsigned quantum = (sz > cy_blanks_len)? cy_blanks_len: sz; sink_write_do(s, cy_blanks, quantum); sz -= quantum; } } void /* f() is basically the same as printf() */ cy_sink_f(cy_sink* s, const char* fmt, ...) { /* f() */ char temp[ 2048 + 2 ]; va_list args; va_start(args,fmt); vsnprintf(temp, 2048, fmt, args); va_end(args); temp[2048] = 0; /* ensure end nul */ sink_write_do(s, temp, strlen(temp)); } void cy_sink_fn(cy_sink* s, const char* fmt, ...) { char temp[ 2048 + 2 ]; va_list args; va_start(args,fmt); vsnprintf(temp, 2048, fmt, args); va_end(args); temp[2048] = 0; /* ensure end nul */ sink_write_do(s, temp, strlen(temp)); cy_sink_n(s); /* newline indent */ } void cy_sink_ft(cy_sink* s, const char* fmt, ...) { char temp[ 2048 + 2 ]; va_list args; va_start(args,fmt); vsnprintf(temp, 2048, fmt, args); va_end(args); temp[2048] = 0; /* ensure end nul */ sink_write_do(s, temp, strlen(temp)); cy_sink_t(s); /* tab */ } void cy_sink_ftn(cy_sink* s, const char* fmt, ...) { char temp[ 2048 + 2 ]; va_list args; va_start(args,fmt); vsnprintf(temp, 2048, fmt, args); va_end(args); temp[2048] = 0; /* ensure end nul */ sink_write_do(s, temp, strlen(temp)); cy_sink_t(s); /* tab */ cy_sink_n(s); /* newline indent */ } void cy_sink_nf(cy_sink* s, const char* fmt, ...) { char temp[ 2048 + 2 ]; va_list args; va_start(args,fmt); vsnprintf(temp, 2048, fmt, args); va_end(args); temp[2048] = 0; /* ensure end nul */ cy_sink_n(s); /* newline indent */ sink_write_do(s, temp, strlen(temp)); } void cy_sink_nft(cy_sink* s, const char* fmt, ...) { char temp[ 2048 + 2 ]; va_list args; va_start(args,fmt); vsnprintf(temp, 2048, fmt, args); va_end(args); temp[2048] = 0; /* ensure end nul */ cy_sink_n(s); sink_write_do(s, temp, strlen(temp)); cy_sink_t(s); /* tab */ }

11apr09 chimera hunts

context

     (See the fiction page first for context.)

grand canal

     Eli loved the water, whose color under a bright sunlit sky had slightly unreal greenish tones over royal blue. Here in the middle of the canal it was very quiet: the city seemed deserted. In this silence Eli could listen to the breeze and the oar dipping into the water as Zé played gondolier. The smell was very refreshing.

     Zé said the real Venice wasn't nearly as clean as this one, but a stink didn't have the right ambiance until you hit places in back canals. Faith to reality blocked themes Zé wanted to explore, so colors were richer, treasures larger, villains trickier, and allies worthy of friendship. So far, only Eli's past experience in other games supported the hypothesis none of this was real. But Eli suspected this world had a pervasive emotional undertone the same way movies had sound tracks: a subtext of wistful longing. If dreams of youth fled, this is where they would go.

     Eli turned away from the view of the palaces on the canal to study Wil, who sprawled peaceably on the other side of the gondola, napping or meditating to savor this contrast with action to come once they started an opening scenario. Wil wore dragon skin nano armor, but a sort subdued in design and color—washed out tones blending well in many contexts, in a style avoiding fashion of any time period. Wil's neck, arms, and legs were tanned and sunburned, heavy with muscle. Wil had a short Vandyck beard wallowing in several days stubble and unkempt curly hair of medium length lightened by sun. A flexible helmet, modest and compact, folded over his belt. Wil's clear wraparound glasses were stowed away in a pouch lined with fine copper wire mesh. Wil opened his eyes.

     Wil smiled and asked, "Ready for Venice 3.0?"

     He seemed about twenty four years old, give or take a year or two—still older than Eli, but only barely. A scar across Eli's eyebrow and nose aged him more than the two years since. Eli refused to have the scar fixed, to remind him of his loss. Zé accused Eli of nursing his pain; Wil had no comment. The scar paid dividends: folks checked Eli's eyes for a spark of anger which waxed and waned according to context.

     "I still want a motorboat chase someday," Eli said. "Zé owes me a James Bond canal chase."

     "This is so much better," Zé countered, then glanced at Eli. Zé drove the gondola onward without effort—he looked as young as Wil, but not as strong. Zé grinned and asked, "If I put a virtual Venice boat chase in the story I'm writing, does that pay off my debt in full?"

     "No!" Eli glared. "But I'll accept a spectacular foot chase over roof tops and bridges today, provided the firefight is good and Wil doesn't reduce legions of bad guys to quivering piles of injured flesh all by himself."

     "Your wish is almost certainly granted," Zé smiled knowingly. "Wil can't save you in this one, and vice versa, though it does help to stick together."

     "How's the story coming so far?" Wil asked. "Eli said drafts were good. But he wants to revise the scene where Finch trees Ron Toss. Are you almost up to the present? Or still stuck back in my apartment?"

     "I'm about to start the scene where Finch studies my ring the night she brought the gold," Zé said. "It's going to be hard to convey just how surprised she was. And after her errors, how can a reader think she finally hit the right idea? Especially given how it sounded?"

     "Your ring still gives me the willies," Eli said, pointing. It looked almost like it did the first night, just a bit larger and brighter, but its presence weighed on Eli now he had some idea what it was.

     "You went and said its name: now I have to edit your remark," Zé chided. "I'll say you said 'your ring' instead to keep the name for later. Not everything you heard about it is true. I still seem pretty normal, don't I?"

     "Yeah, so far," Eli granted. "Just don't do anything spooky, like let your eyes glow and speak with a deep echoing voice. Then I'll avoid thinking about it."

     "He's thinking of a Star Trek episode," Wil told Zé, snapping his fingers. "In a second pilot, Where No Man Has Gone Before, crewman Gary Mitchell gets eerie powers from a visit to the energy barrier at the edge of the galaxy. His eyes get a silver glow."

     "Those pesky energy barriers," Zé shook his head at Wil. Zé smiled at Eli and said, "I can't do any fancy shit like that, Eli. I'm still me, honest. And even if I could, actions speak loudest of all: I'm a good guy."

     Eli cleared his throat. "Sure, but your games are bigger. And they keep running when you aren't in them. Where is this place? Can it vanish while we're inside, taking us wherever ended games go? And what will you be able to do in a few years?"

     Zé paused to think, running his eyes over mansions lining the canals. "You're safe: these games are very stable," he assured. "As for me, there's probably a limit to how much you can learn and still make sense to others. I don't know the future."

     "You extrapolate a lot from nothing," Wil told Eli, then went on to tease Zé. "Besides these games, so far all he's done is write barely tolerable fiction. Of course, I'm skipping his conquest of the lava men."

     "Why did you mention editing?" Eli asked. "Are you writing this into your story?"

     "Yes," Zé nodded. "I have self referential parts since my project was to write stories. So far I haven't included any written by you guys, but I will. After this scene I can tack on a fragment of yours, Eli. But, why does your tone veer to violent and/or overdone?"

     "Eli thinks subtlety is for wimps," Wil said.

     "Maybe Hollywood movies ruined my sense of proportion. Next you'll ask me to stop giving characters my scar," Eli complained, pointing at his face. "It works: Everyone learns a lesson and gets a scar."

     They arrived near Piazza San Marco on the seaward side of the plaza; Eli jumped out to tie a line. No human beings were in sight. The plaza was deserted of life except pigeons. Eli hefted his stuff and prepared to hike. The architecture was beautiful.

     "I suppose we have to figure out what happened to everybody," Eli muttered.

     "Oh crap, what is that?" Wil asked. Eli looked where Wil pointed: on top of the Doge's Palace, a man-sized creature with antenae waving slowly watched them silently. It looked like a humanoid crustacean—maybe a man-lobster or something.

     "Doesn't look so tough to me," Eli judged.

     "Those are just the beginning," Zé smiled. "Your first run is in hero mode, so be careful where you shoot. There's a big penalty for destroying things without need, and the place is filled with priceless artifacts."

     "Can I peek inside without getting killed?" Eli asked, walking backwards toward the Doge's Palace.

     "Sure," Zé nodded. "I'll see you when you're ready for a break. Good luck figuring out the rules."

     "I'll be right there," Wil told Eli. "I just want a word with Zé. Keep your weapons handy."

     The two watched Eli head the way. "He's grown up a lot," Wil said. "Do I have long here before something comes up? Should I relax?"

     Zé smiled and his eyes silvered over just like Gary Mitchell in Star Trek. He replied in a space-filling voice, "I'll keep my eyes open."

     "I heard that!" Eli shouted back playfully.

     Zé's eyes returned to normal. "So many things are happening. But take time off. Don't worry."


     (edited by Eli)

     "It's my lucky day," Finch grinned savagely, looking at the park next to Vintage Season. "I've been looking for this scumbag. It's payback time."

     Wil followed Finch curiously. "You aren't going to ... hurt him are you?" he asked hesitantly.

     "Not as much as he deserves," Finch smiled over her shoulder. "A week ago he ripped Miss Morlock a new one. Now I can return the favor. Need a soundtrack? Tell your glasses to play Wrath of Miss Morlock."

     "Play Wrath of Miss Morlock?" Wil echoed. To his surprise a song started playing on his glasses. It sounded like The Crystal Method ... it was Trip Like I Do, but re-edited and ramped up a little; the lame girl's voice was missing. Now it just delivered driving base.

     ... (several paragraphs elided) ...

     Finch turned Ron around and grabbed his belt at the small of his back. Picking Ron up effortlessly, Finch spun in a circle, swinging Ron around like a track and field athlete winding up to throw a discus. At the height of his third orbit, Finch released Toss's belt, lofting Ron up into the tree where he scrabbled for purchase like a kitten.

     "Okay, now you're scaring me," Wil said.

06apr09 whiny consumers

editing

     Here's an example of editing for length. I've taken a paragraph from Dave Troy on idea gardening (a title that sounded fun), shown below in italics. First let me strike out words I'd remove when I seek brevity as a goal. My goal here is to preserve original meaning exactly.

     But the idea itself — the raw two or three sentences that define your concept — has very little potential by itself. By sharing your idea with others, you can strengthen it. Others can contribute to it, pointing out the places where it's weak, and repurposing it in ways you never imagined. Don't be afraid to share your ideas, in whole or in part, so that others can help you bring them to fruition.

     The Strunk & White rule is something like: if you can remove a word, then do so. I just ask myself if each word adds anything, or if a sentence means the same thing with a word missing. I can almost always remove that from a sentence. Now here's a light rewording, which might alter meaning a bit. (A big change: removing raw; it conveys something essential, but it might be implied.)

     But an idea itself—two or three sentences defining your concept—has little potential by itself. By sharing ideas with others, you strengthen them: Others can contribute, point out weakness, repurpose in ways you never imagined. Don't be afraid to share ideas, in whole or in part, so others can help you bring them to fruition.

     Thanks for the nice idea, Dave.

direction

     The next installment will be a flash forward, for several reasons. One of the reasons is a topic of discussion by the characters: a few critical issues are supported only by Finch, and she's not terribly demonstrative. And it's hard not too worry she crafts disinformation.

     Most scenes begin when a character says something about a topic. Think of them as mumbling all the time in the background, like a crowd with no lines. The murmur is empty of meaning. Until one actually says something funny. Usually it's funny. To me anyway. So writing is like dreaming up jokes.

     So the flash forward is because Zé et al bandied about a title for the story. The funniest is Eli's suggestion, Zé and Wil Get Pwned, which ought to only appear in situ, since showing it here takes steam out of that scene. But I'm trying to convey an idea how my outline works: I don't have one, other than spontaneous ideas. However, I have a wish list I try to work in all the time, like software features demanded by managers.

     It's not clear to me who's reading. I either have a very small audience, or a slightly larger one lurking quietly. The only thing suggesting anyone reads at all is coincidences in topics I see elsewhere—which is just as easily explained by selective attention: I'm likely to notice anything related to what I say.

     I might just be writing for myself. As a way to relieve boredom, writing fiction works far better than I guessed. It's great. But I'm almost antsy to code more now. If you're reading and you want more, you might want to make yourself noticeable; else I might cut you off and write code instead if I don't see you.

05apr09 imps of perversity

pulp fiction

     The more I write this story, the more I wish I took better care of it. But it's just amateur pulp fiction. It falls close to juvenile territory. And it's a little cartoonish, because it's rushed. Each time I write a segment, I like a lot of it, and dislike a lot too.

     I only bother because I enjoy liking the story. But it's aggravating to the extent I hate the result of not doing a good job, instead of hacking away as I am. I whipsaw back and forth between smiles and winces.

     I sense material now is still backstory for future plots. Pace in later stories might rush less. I'm trying to empty my queue now; it's an excuse for blitz throw-away banter: characters hardly take time to reflect.

context

     (See the fiction page first for context.)

zoetrope

     As he drove back, Zé called up Red's face and studied it carefully, stopping short of a full bore alternate reality visit. Daydreaming on the road was bad enough without leaving the car completely. But he wanted to talk to her again. If only she hadn't worn sunglasses. She seemed familiar, like someone he had known long ago, but forgotten.

     The sensation was was catnip to Zé, like nostalgia for an age that never existed.

     Extremely fine gold lines gleamed faintly on his right ring finger in the car's dark interior. At the next stoplight Zé reached in a tray by the gear shift and found an old ring shaped like a snake devouring its own tail: an image of Ouroboros in steel; he couldn't find one in silver, so this had to do.

     For comparison Zé put on the snake ring too, and when it touched the gold ring, the two clicked together as if attracted by magnetism. He could pull them apart, but they attracted one another. The gold ring couldn't have any magnetic alloy, could it?

     The double ring was surprisingly comfortable, so he left the snake in place when the light turned green. If Zé had watched long enough in good light, a change would have been visible soon: the snake ring was slowly being eaten where it touched the gold ring, at a steady but miniscule pace.


     Ulf gargled mouthwash then studied himself in the mirror after spitting. He parted his hair again and studied his roots. Sure enough, it was growing in dark again, everywhere. It wasn't his imagination. Can anything at all cause that to happen?

     Ulf bared his teeth in imitation of Jim Carey in The Mask, which he just watched last night, rendering his verdict with a shake of his head: "Smokin'!"

     He used to hate comedies, but now he had an appetite. Jim Carey was hilarious, so why had Ulf always hated him before? So far he refused to admit it might be related to his late outbursts of sarcasm at work—no, actually all the time—because that would hint at some ... neurological basis.

     A trip to the doctor might be in order. But then he'd have to include the dreams as a symptom, too. Ulf could already hear his doctor's voice: We need more tests, but there's nothing to worry about, Mr. Varg, so no need to put affairs in order.

     More than anything else, it was a growing sense of knowing what might happen that scared him: it meant his brain was acting up big time. It felt like having a word on the tip of his tongue. Only instead of a word, it was a glimpse ahead. When he recognized things he foresaw, Ulf knew it was just circuits in his brain firing wrong, making him think it was recognition.

     Ulf grabbed his keys and headed to Wil's.


     For ten minutes Eli and Wil ran around the living room, firing at each other with wild abandon, like Neo and Agent Smith in the subway scene of The Matrix. Or at least, that's what Eli imagined. So far they hadn't broken as many things as you'd expect. Wil had disturbingly fast reflexes, and Eli suspected he was pulling his punches.

     When Eli stopped for water, Wil grabbed a set of three darts and stood six feet away from his dartboard in the main room. Eli watched Wil compose himself, slowly take aim, then let the first dart fly. It was a bullseye, so Eli applauded.

     Wil looked at Eli with a keen expression. "How many times did I throw that dart?" he asked.

     "Just once," Eli tried not to laugh. "Is that your impression of a schizo? Get a grip man."

     "Had to check—watch this," Wil smiled. Then he put the next two darts in the center as well, crowding the bullseye. Eli clapped and hooted.

     "Crap," Wil said in dawning realization. "This isn't going to be fun anymore. I guess I could make it harder. Hey Eli, watch this."

     Wil gathered the darts and crossed the room, getting as far from his dartboard as he could—about twenty feet or so—then with great care, threw the first dart across the room into the bullseye again.

     "Damn, Wil," Eli praised. "Whatever you're doing, it's not fair. And you're being a bit of a show off."

     Wil flushed slightly red. "Sorry, I was studying a skill. Didn't mean to demand attention. How do you like your armor? I think it's bullet proof."

     "It's great: comfortable," Eli grinned. "But it couldn't be bullet proof—it's not strong enough."

     "I'll look into it," Wil promised. "So far I'm only certain it turns a steel dart, and then only when it hits a point right below my breast bone. Not enough data."

     "So you know at least one sheep is black—on at least one side?" Eli joked.

     "Yeah," Wil smiled. "Hey, I have a polaroid I want to show you. It has a Youtube video inside."


     Finch skipped models firing depleted uranium because a little aerosolized dust can haunt you a long time. You get fine tradeoffs in mass, energy, and wind resistance in other forms, too, when all you care about is getting business done. She picked her favorite: good rate of fire, but not a gatling gun to conserve ammo.

     Flywheel chose overkill when he could, but Finch loved the art of using exactly the right amount of force.

     After enabling the bot, Finch ran it through tests before arming it with different magazines and extra spares. It's battery was already full, but she inserted an extra the size of a thimble as a backup—it had energy density less spectacular than models for long lived purposes, but could release it very rapidly without high loss.

     Since Wil needed them, Finch pulled two top grade guns she liked the most: one passed for contemporary, but the other was a match for a piece she pulled on Marco and Tyler some months ago, useful for intimidation when you wanted to signal you weren't fooling around.

     It was conceivable either Eli or Zé might use one in a pinch, if things went pear-shaped, so she grabbed another just in case. Then after a moment of thought, Finch smiled and grabbed a couple more surprises.

     The last burden was heaviest, but Finch hefted the saddlebags over one shoulder without strain. Heavy coins shifted and clinked inside as she settled it in place. She had two main reasons to fetch it now, only one of which she planned to tell Wil. The secret reason: to defuse criticism and make it harder for them to think, especially Ulf, who dreamed of nothing more than riches. While this wasn't a lot, it was more than enough to serve its purpose.

     Officially, Finch's reason to bring it to Wil was ordinary financial concern. As soon as Wil believed he was getting younger, he was going to ask himself what would happen if he went to work aged only 25, instead of 45. Obviously, he was going to lose his job, unless he convinced his company he was really Wil. And if he did that, all hell would break loose, culturally speaking.

     But the money aspect was just a red herring. Even worse might be Wil's realization there was no point in going to work. Long nurtured habits die hard.


     Ulf stepped through Wil's front door and froze when he found Eli pointing a toy gun at his chest while decked out in this absurd costume complete with streamlined future bike helmet and wrap around glasses.

     "Freeze earthling!" Eli commanded in a voice clearly copied from something he heard in a movie. Eli's big smile slowly faded, as if he had expected a satisfying yelp from his father. (But Eli thought Ulf acted like he half expected it—which was impossible because Ulf never expected anything, making him so fun to tease.)

     Wil waved from the sofa. Ulf groaned when he saw his matching costume; but Wil's was a deep blue in contrast to Eli's very dark red. Still, they looked like refuges from some red versus blue game, like a couple online dorks hell bent on playing laser tag like it had anything to do with the real world.

     "What are you guys doing?" Ulf asked.

     "Watching future home videos on a polaroid photo," Eli joked with glance over his shoulder at Wil.

     "Whatever," Ulf rolled his eyes. Then he looked at the floor to see what had caught his eye.

     "We're watching Zé and Wil's Excellent Adventure," Eli prosecuted his jest even further. "You're not going to believe this, Dad. It's amazing."

     "What are you looking for?" Wil asked Ulf.

     Ulf smiled apologetically. "I thought I saw a gold coin roll across the floor," he explained. Then he smiled, "You didn't rob a payroll train, did you?"

     "Just a glitch in the matrix," Eli smiled gleefully.

     Ulf studied Eli's gun, deciding it was more real than he first thought. "What does that shoot?" he asked Eli.

     "Pellets. You should try it," Eli urged. Then he grinned impishly and suggested, "In fact, I dare you to try facing off with Wil in a fast draw contest."

     "That's not a good idea," Ulf told Eli. But Ulf heard an undertone he didn't like: mercy on an old man.

     "Gimme that gun," Ulf held out his hand for Eli's weapon. Eli surrendered it gladly. Ulf glared at Wil, saying, "I've seen Dirty Harry a dozen times."

     "You better empty your bladder first," Eli warned.

     "You insolent snot!" Ulf returned. "I'm saving up my piss to put out a building fire later tonight."

     Eli faltered in surprise. "Your sense of humor is a lot better," he noted. "Are you okay?"


     Entering the brightly lit foyer of Wil's building, Zé glanced at the two rings on his right hand again. He stopped in his tracks, raising his hand close to his eye.

     The Ouroboros ring was half gone. The gold ring nestled inside it where they touched.

     They still separated when Zé pulled on the snake ring, but clicked together magnetically when he let go. He went close to a light and studied the rings and his skin, looking for ... for tiny steel bits of grit he expected to see if the gold ring was wearing the other away.

     Nothing. All was squeaky clean. Whatever happened to the steel ring, there was no sign of it.

     A tiny panic hit him, but he squelched it: No harm, no foul, right? It just meant the ring was a marvel, not just a trinket from a Cracker Jack box.

     Zé bounced up the steps to show Wil before it was gone. He flew up three stairs at at time, reaching Wil's floor without any loss of breath.


     Kip has another picture of Finch, he's sure of it. But match is poor—low confidence because detail is lacking: the image is just too small even though it was taken using a telephoto lens. It shows a woman with dark hair in a snood wearing blue jeans and sunglasses, studying a position on the grassy knoll of Dealey Plaza in Dallas, Texas in 1963. It's one of many random photos in a large collection related to the Kennedy assassination.

     Her dimensions match Finch's as closely as Kip can measure. It might convince no one but Kip, but he doesn't think blue jeans were common in 1963—not in Texas. This part alone looks wrong. Anachronisms are circumstantial evidence as far as Kip is concerned. His amusing theory to explain Finch's presence at Woodstock in 1969 is plainly nonsense, but he stopped letting preconceptions throttle his theory creation long ago.

     Camera feeds from Wil's place have been very engaging today since Wil and Finch returned after sunset. Eli's right: Wil looks different; Kip might quantify it with software later. Watching Wil outdraw Ulf is a thigh-slapper: When Kip replays it in slow motion, Wil finishes aiming before Ulf even gets his hand close to his gun. Kip wishes Wil fired instead of stopping when Ulf faltered. Ulf's attack of nervous humor afterward isn't nearly as fun, but it's better than nothing.

     Zé enters and nears a camera, looking at Kip through the lens. Kip bumps the sound as Zé says, "Zé to Kip. Testing, one, two, three. Please respond."

     Kip hates when Zé breaks the illusion of anonymous surveillance. Reluctantly Kip flips a switch and says, "Kip to Zé. Proceed with operation Full Disclosure."

     "Just wanted to know if you were there," Zé replies. "I can only try. So, as you were, Colonel Kip."

     Kip snaps off the switch and fumes.


     "Do you always converse wtih appliances first after you enter?" Wil asked Zé.

     "Special circumstances," Zé turned to Wil, then stared. "You used to be a little older than me."

     "I thought you'd want to talk about my cool toys first," Wil held out his arms. "Nano tech dragon skin."

     This was the tone of voice Wil used to pretend he was joking when he really wasn't.

     "No shit?" Zé raised his eyebrows and went over to check his email. The message from Kip with Finch's picture at Woodstock in 1969 should be there.

     "Yeah, part of my weird story," Wil nodded.

     "Here's part of my weird tale," Zé held out his right hand so Wil could see the gold ring and what remained of the steel Ouroboros ring.

     Wil could see incredibly thin glowing lines flow in the gold ring. His glasses also highlighted the ring with a strange outline, like a warning. Wil leaned in closer to get a good look. Finch's voice sounded tiny in his ear: the glasses or the helmet were wired for voice.

     "Don't touch the ring, Wil," Finch said.

     "Crap, I knew you were bugging me," Wil sighed.

     "I'll be there in two minutes," Finch promised. "Just don't touch it. Basic virus hygiene."

     "Who are you talking to?" Zé asked.

     "Wait, there's a joke in here if I can find it," Wil stood up straight again as Zé brought up his email. "A little bird told me not to touch your ring."

     "Finch?" Zé asked. "Those look like a clear pair of her wrap-around glasses."

     A picture of Finch came up in Zé's email agent, along with a link to a source web page with other Woodstock pictures taken in 1969 during and after the concert. Kip's description was accurate: it was definitely Finch and some of those guys looked like Hells Angels.

     "Yeah, a gift from Finch," Wil confirmed. "If I had to guess, I'd say she can see everything I see."

     "Speak into the flower vase so I can hear you clearly?" Zé asked. "Kip told me about this picture right after I left Flywheel's office. It's Finch at Woodstock."

     "I'll be darned," Wil said. "That actually makes me feel better. But it's not nearly enough confirmation."

     Ulf stood behind Zé looking at Finch's picture. "I was just a kid during Woodstock," Ulf said quietly. "Bring up some of those other images. Maybe it's fake."

     "Isn't that your Ouroboros ring, Zé?" Wil asked. "It's two thirds gone, though. Does that mean ..."

     "Yeah," Zé nodded. "The gold ring is eating it."

payday

     Finch returned with a heavy load, making two trips from the hallway. First she carried in a model of some kind of low-slung robot standing knee high when she set it down, making Zé cheer like it was Christmas. Ignoring questions, she stepped back out.

     The second load consisted of saddlebags making the floor creak when Finch set them down. How did Finch get all this upstairs? The saddlebags hit the floor heavily like bricks, slumping over with clinking sounds.

     A single bright coin fell out and rolled on its edge across the floor, giving Ulf a case of déja vù. Eli leaned over and caught it as it rolled while Finch went for a camera and controls to turn them off.

     "I know you can hear me, Kip," Finch told the camera. "I need you here—don't make me come get you. You have till morning, then I'll be mad. Yes, it's me in the photo. But tell no one or I shall be put out."

     As Finch shut down the cameras and pulled their wires, Ulf approached the saddlebags and tried to move them, but they didn't budge. Ulf peeked inside and stared.

     Eli showed Wil the big coin he picked up. "It's gold," he said. "Dated 1857. The bag must be full of them."

     "It's a $20 gold piece," Wil said. "They're called double eagles. Worth, oh, a thousand bucks apiece now."

     "I went to heaven," Ulf closed his eyes, smiling.

     "Those bags are filled with gold," Finch smiled. "Over a thousand double eagles—say twelve hundred."

     "You robbed a payroll train?" Wil guessed.

     "No, I robbed guys who robbed the train," Finch said. "I sorted them out, then indemnified Wells Fargo in diamonds: they weigh less."

     "That's what I always say," Zé chimed. "Diamonds are so much easier to transport."

     "I robbed a train before lunch," Ulf claimed. "What's the big deal? I just left my booty home."

     "That's a lot of gold coins," Wil mused.

     Eli had a nervous look. "When you say sorted them out, what do you mean by that?" he asked.

     "Not everyone who does a bad thing is a bad person," Finch explained. "So I had to check."

     "When you sort good and bad people, you put them in different piles," Ulf told Eli with a grin.

     "What year was the robbery?" Zé asked, then pointed at Finch's picture, still on the monitor. "You told Kip you were at Woodstock? In 1969? Really?"

     "Yeah, I had something to do there," Finch confirmed. "I didn't go to Woodstock for the concert specifically, other than making sure it turned out okay."

     "Honey, I need to drop by 1969 and pick up some milk. Do you want me to get you anything?" Ulf asked.

     "Dad, why don't you count the gold in the bags and let Finch talk for a while?" Eli urged. Eli shrugged and joined Wil, who was simply watching, happy to let Zé draw Finch out further—if he could.

     "What needed doing in 1969?" Zé asked.

     "Know how many people attended Woodstock?" Finch asked. "About half a million. Do you know what happens when a riot happens in large crowds?"

     "Don't bad situations require a tight place for crowds to squeeze themselves?" Wil asked doubtfully.

     "Compressive asphyxiation," Finch nodded. "Either by piling up in layers, or getting crushed vertically against walls or rails. With enough pressure, people simply can't breathe. Panic and the wrong direction does it."

     "I thought almost no one died at Woodstock," Zé looked at Wil. "Maybe a couple accidents?"

     "157 people died at Woodstock the first time," Finch said. "They crushed down the hill into the bandstand corner. It was called the Woodstock disaster."

     "I stopped a volcano from erupting before breakfast," Ulf laughed. "All it took was a few icebergs I flew down from the icecap. But do I get credit for things that didn't happen? No, you rest secure in ignorance."

     "Did Falk do something to Ulf?" Wil asked Finch.

     Finch nodded. "When some older patients get their sense of humor fixed, they try to make up for lost time," she said. "Sarcasm is typical."

     "Let's do one story at a time, okay?" Zé begged.

     "What if that's his sense of humor?" Eli worried.

     "Sorry," Wil said. "Did you change the outcome at Woodstock somehow? And more to the point, why didn't Chthon stop you? Hold on a minute, Zé."

     "The riot was widely considered unstoppable," Finch smiled grimly. "The cause was too complex to stop, and if someone did stop it, a riot would be easy to induce on purpose with agents to make history come out correctly, if really necessary."

     "So you not only had to stop the riot, but also the agents who showed up later?" Wil asked.

     "Yeah," Finch said casually. "No one from Thule got anywhere before. It was thought hopeless."

     "I want some of whatever she had," Ulf said.

     "Shut up," Zé told Ulf without heat. "Thule?"

     "I've been told—in the vaguest possible terms—that Finch might be from a place called Thule," Wil explained. "It's like a codename for something she won't talk about. So Thule is a somewhen, not a somewhere. Finch winces when I talk about time travel."

     Finch winced. "If you keep saying that, this talk is going to be frustratingly brief," she threatened.

     Wil spread his hands to say: see what I mean.

     "The opportunities for comedy are endless," Ulf pleaded. "Please, I need to go on a riff."

     "Try to hold it dad," Eli said. "You can save it up and put out a burning audience later."

     "There's something funny bubbling up, though," Ulf said. "Something from ... Saturday Night Live."

     "I already did that one," Wil told him. "So it's stale now. You can take another shot later."

     Zé clapped his hands. "Silence!" he ordered. "How far away is this Thule, approximately?"

     Finch looked at Wil, who shrugged and made a keep going gesture repeatedly with one hand.

     "Way the hell out there?" Zé translated. "Like, we have all the answers, super science type out there?"

     "How am I supposed to figure that out empirically?" Wil asked in chagrin. "What can I test?"

     "You can go for a visit," Zé suggested. But Finch shook her head decisively.

     "Aren't these wild premises to take for granted?" Eli asked Zé. "You haven't even seen the photo yet."

     "You showed him," Finch glared at Wil.

     "Going to pretend you didn't know that already?" Wil accused. "You watched through these glasses."

     "So when is the gold from?" Zé asked again.

     "1870," Finch said. "You won't find any mint marks after that date. There's a lot more."

     "More gold?" Ulf asked, like it was ice cream.

     "We're gonna be in a world of trouble, aren't we?" Zé realized. "First we get told the good news: at least there's lots of gold. Then comes the bad news."

     "Actually there's more good news," Wil said, but that just made Zé look even more worried.

     "I know what it is," Ulf looked up in wonder. "The predators are going to come for us, aren't they? Every eco system has them. Time wolves want to eat us."

     "Every eco system has lots of things," Finch said. "You can find an analog for anything you consider. Khronos giveth and Khronos taketh away."

     "Time travel fatalism from Thule," Ulf sniffed.

03apr09 solving for unknowns

context

     (See the fiction page first for context.)

trust

     Wil stopped in the stairwell. "I trust you enough to wear an unlicensed nuclear accelerator on my back," Wil turned and told Finch. "You need to trust me too, or this isn't going to work. What do you say?"

     (Continued at fiction.)

darts

     Eli didn't recognize Wil for several seconds and almost asked who he was. It wasn't any single thing, just too many odd details at once.

     (Continued at fiction.)

02apr09 locally consistent

war zone

     "Why didn't you just whip off your glasses and let Zé have it?" Wil asked. "Tell him not to involve Flywheel in his stories? Was that too easy? Or did you have another reason to join our project?"

     "I tried that," Finch sighed. "Paradox got out of hand: he didn't write the same stories—not enough anyway, and things changed too much. I had to undo it. Flywheel and his stories are entangled now, going forward."

     "So what? Does he invent the flux capacitor, or something?" Wil doubted. "You said time was non-deterministic: change doesn't alter the future. But now you say some changes do. Which is it?"

     "Good question," Finch said. "But hard to answer. You know how light is like waves and particles, both?"

     "Oh, Jesus," Wil slumped. "Quantum theory?"

     "No, just as metaphor," Finch corrected. "More than one plausible theory makes sense, but none is complete. Empirically we find local consistency only. In a local time context, cause and effect is consistent—except for folks like you and me—but frames of reference far apart can see multiple inconsistent outcomes."

     "Riiiight," Wil digested this. "The upshot is ...?"

     "Know about multiple world theory?" Finch asked. "Like parallel universes? Differing variations?"

     "Yes," Wil assured. "Jump to the end for me."

     Finch held up her hands to frame this explanation. The sunset gave her an eerie color, fixing this moment in Wil's mind. Later, after hearing Zé tell his story about Red and Alley Oop, Wil recalled this moment to see if Finch had any idea. Apparently not.

     Finch explained slowly and carefully: "You know two science fiction templates about time: one with a single consistent time line rejecting paradox, and a second template with N totally independent parallel time lines. But in fact, both of these are true in some distribution across observer frames—hard to map—and it changes all the time, like a voting effect among time lines tipping toward a consistent view. Without traveling in time you can only see it by finding two variations of history that can't both be true, and yet they are anyway. Nature abhors a paradox, but it happens anyway—some of the time—and you can't predict when."

     "Gee, so all the operating systems for time suck?" Wil translated. "Is it a free-for-all?"

     Finch sighed. "Things are generally stable on small scales, but in flux on large scales," she said. "It's easy to trip far, but very hard to trip near: it conflicts with local consistency. As you guessed, there's a war. Of sorts. It's not that organized. Can you guess main sides?"

     "Static Chthon vs dynamic Thule?" Wil guessed.

     "Oh, you're good," Finch smiled. "But there's a lot of dynamic camps. Thule is just the best one."

     "Crap, it looks like we're here," Wil pulled up in front of Vintage Season. "I was just going to ask if you're human. What are you?"

     "An agent never tells," Finch teased. "Okay. If you ignore provisos I don't have time to cover, you would say the answer is yes. Unless I'm lying."

     "There's always that," Wil rolled his eyes.

     "It's my lucky day," Finch said suddenly, looking at the park next to Vintage Season. "I've been looking for that scumbag. Wanna see me tree a journalist?"


     Wil trailed in Finch's wake. "You aren't going to hurt him are you?" he asked tentatively.

     "Only his pride," Finch smiled over her shoulder. "A week ago he ripped Miss Morlock a new one. Little prick's been on my list."

     Wil worried until he saw her target: Ron Toss, who once wrote about Wil's last startup—stupidly. Ron wrote a vicious column called Tossing Out the Trash. Toss was sometimes called The Defenestrator, which made him sound bigger than he was.

     Toss saw them coming: he put on a casual sneer. A girl sitting at his table drank coffee, but Finch tipped her sunglasses down to look at the girl and hooked her thumb, saying, "Restroom." Then Finch put her glasses back up to confront Toss.

     "I wondered when you'd show up," Toss smiled with a trace of venom. "A rumor said Morlock had Agent Finch for muscle lately. Morlock should do her own dirty work. Were you two sorority sisters?"

     Finch stood over Toss, who sprawled fearlessly in his chair. Wil was afraid Finch might kick the chair out from under Toss. "Apologize, Ron," she said.

     "Bite me," Toss smirked.

     "Take it easy on him," Wil urged.

     "Who's your sidekick?" Toss turned to Wil. "It looks like Bicycle Boy! Where's your bike?"

     "He's all yours," Wil told Finch, stepping aside.

     Finch took off her sunglasses and Toss looked like he was bitten and wanted to tell Finch, locked on her eyes. "Can you meow like a cat for me?" she asked.

     "Meow," Toss did a passable cat impression. But he looked somewhat puzzled.

     "More like a kitten," Finch suggested. "Plaintive."

     Toss mewed with vigor.

     "Perfect," Finch told him. "Keep doing that after I help you up in this tree. Make sure you stay loud enough to call for help. Don't answer questions, and climb higher when they try to get you down. But don't go so high it isn't safe. Wait until firemen come and get you."

     "Why do I have to do this?" Toss wondered.

     "And we weren't here," Finch added. "You don't remember us. Okay, let's get you up in this tree. I'll give you a leg up."


     Finch put on her sunglasses, then went inside and told Ollie to call the fire department.

     "That was mean," Wil said, smiling.

     "Somebody's gotta do it," Finch said and walked across the street to Wil's apartment. "Maybe Mr. Incredible will come by and shake him down."

01apr09 caves of steel

espionage

     Wil surprised himself by driving calmly home again after his training exercise in the paintball facility. Coming back outside into broad daylight was a shock. Finch rode shotgun, studying Wil's profile—which yesterday would have made him feel nervous. Now his scale was totally different. He even relaxed a bit.

     "Any chance a bunch of abusive monks in a martial arts monastery raised you?" Wil asked Finch.

     "Did I treat you poorly?" Finch wondered.

     Wil pulled his lip with one hand. He still wore Finch's glasses because she said they were now his. Either the glasses were sharpening odd choices in the street, or Wil's mind was playing games. Maybe both. That would make sense if Falk's therapy was interactive.

     "Ordinarily I would say yes," Wil replied. "If I knew more context, I might say I needed that. You give off a dire pending emergency vibe. Anything you feel like sharing? Should I watch for boogie men?"

     "Ever read Eric Ambler novels?" Finch asked.

     "Is it indirect catechism time?" Wil sighed.

     "Some folks credit him with inventing the modern spy thriller," Finch explained. "I'd like you to try his 1939 A Coffin for Dimitrios because you might relate to the main character—a guy named Latimer finds himself interested in writing a book about a dangerous dead guy he learns about: a shadowy figure named Dimitrios."

     "Let me guess," Wil anticipated. "Latimer's bored and starts researching a colorful assassin named Dimitrios, and it seems like an acadmic exercise because, after all, Dimitrios is dead."

     "So you read it?" Finch teased. "A colonel in the Turkish Secret Police sets him on the trail of Dimitrios, who turns out to be fascinating, so Latimer digs deeper and deeper into his colorful history."

     Wil groaned. "Until his research attracts enough attention that Dimitrios shows up asking Latimer what the hell he wants," he guessed. "Is this your classic 'dangerous guy isn't dead after all' story?"

     "Ambler novels all have a common theme," Finch nodded. "A fairly ordinary person, bored out of his mind with tedium of every day life, gets involved in a deal that sounds a little exciting, just for sake of variety. So they step outside a safe cocoon on a harmless lark."

     "Maybe not so harmless," Wil mused.

     "No," Finch agreed. "For example, in Ambler's 1959 Passage of Arms, an Indian boy finds a cache of weapons he hopes to sell, to finance a bus line he dreams of running as an entrepreneur. An American gets involved in the transaction to pick up a fee and experience a thrill. By the end of the novel he's trapped in a building sacked by British soldiers who throw a grenade into each room before entering with machine gun fire to mop up. Pushed ahead of the purge, he ends up on the roof."

     "I suppose every step of the way it seemed like a reasonable extension of whatever came before," Wil considered. "Did he have any choice?"

     "We often lose choices after we get involved," Finch observed. "If nothing else, we can't choose to unlearn hard lessons bought at dear prices."

     Wil cleared his throat. "You're preaching to the choir. Maybe you can set Eli on this line of inquiry, so he doesn't get himself in too much trouble."

     "All of you are in up to your eyeballs," Finch warned. "You can thank Zé for that, or Flywheel, since having Trippers associate you with him isn't a good move when you write fiction about time travel. A Chthon debriefing is seldom without traumatic impact."