Friday, September 13, 2024

ZigZag Matrix

I was reviewing the implementations of ZigZag on RosettaCode and I couldn't find any that showed how to construct just the coefficient itself, so I made one!

def zz(j, k, n):
    return \
        ((2*int(k//2) + 3)*int(k//2) \
         if 0 == k % 2 else \
         (zz(0, k - 1, n) + 1)) \
         if j == 0 else \
        ((n**2-1-zz(n-1-j, n-1-k, n)) \
         if n <= j + k else \
         (zz(0, j + k, n) - (-1)**(j + k)*j))


if __name__ == '__main__':
    from numpy import matrix
    for n in [6, 8, 10]:
        print(repr(matrix([[zz(j, k, n)
                            for k in range(n)]
                           for j in range(n)])))

Tuesday, June 11, 2019

Early Boot is a Lie

Everything you know about early boot is a lie. The classic introduction to IBM PC compatible (x86, x64) startup sequence goes as follows. Read-only memory (Boot ROM) loads the Master Boot Record (MBR) from the first Peripheral Component Interconnect (PCI) Device. MBR loads the rest of the Operating System (OS). Everyone should write an MBR bootloader (it's fun!), but there is a glaring constraint that is a hold-over from the old days, which is that you have 512 bytes (about 400 available to use for machine code). Here is an example MBR bootloader that closely resembles the "Standard MBR", traditionally written in assembly, but for clarity we have rewritten it in pesudo-Rust.
// Oversimplified Legacy Standard MBR
pub unsafe fn mbr_main() {
    const SECTOR: usize = 0x200;

    // Pointer to Master Boot Record (MBR)
    // This is empty before shadowing, MBR after shadowing.
    let mut mbr = Vec::from_raw_parts(
        0x0600 as *mut u8, SECTOR, SECTOR);

    // Pointer to Volume Boot Record (VBR)
    // This is the MBR before shadowing, VBR after shadowing.
    let mut vbr = Vec::from_raw_parts(
        0x7c00 as *mut u8, SECTOR, SECTOR);

    // Chainload/Underlay/Shadow Master Boot Record (MBR)
    // This program started running from 0x7c00, but the
    // instruction pointer has moved since then, so we need to 
    // jump to here + 0x0600 - 0x7c00, represented by asm_shadow!().
    ptr::copy(vbr.as_ptr(), mbr.as_mut_ptr(), SECTOR);
    asm_shadow!(mbr.as_ptr() as usize - vbr.as_ptr() as usize);

    // Find first bootable partition entry (oversimplified!)
    let bootable = MbrTable::from(mbr).partitions[0];

    // Chainload/Overlay/Fetch Volume Boot Record (VBR)
    csm::ChsReader::from(bootable).read(vbr.as_mut_ptr());

    // Tail call the Volume Boot Record (VBR)
    let vbr_main = vbr.as_ptr() as *const Fn();
    vbr_main();
    unreachable!();
}
The first thing to realize is that since this is the first 512 bytes on the hard drive, that people have written many versions of this. There are MBR bootloaders found in Windows, macOS, Linux, SYSLINUX, GRUB, CoreBoot, SeaBios, EDK2, and many others. There are far too many versions of this to discuss here, and so I will move on to more interesting things. Let's go in the other direction. What loads the MBR? What jumps to the MBR? Enter the reset vector. The code that goes in the the reset vector is called the Volume Top File (VTF). The reset vector is physical address 0xfffffff0 (-16), which has a jump to physical address 0x00007c00 (31744). Sounds like a dream. Here is some equivalent pseudo-Rust:
// Oversimplified Boot Device Selection (BDS)
pub unsafe fn bds_main() {
    const SECTOR: usize = 0x200;

    // Pointer to Master Boot Record (MBR)
    let mbr = Vec::from_raw_parts(
        0x7c00 as *mut u8, SECTOR, SECTOR);

    // Load first sector from first hard drive
    csm::ChsReader::default().read(mbr.as_mut_slice());

    // Tail call the Master Boot Record (MBR)
    let mbr_main = mbr.as_ptr() as *const Fn();
    mbr_main();
    unreachable!();
}
I put my compiled binary code in the first sector of the hard drive, which gets loaded at 0x7c00, and I never have to worry about anything else. Perhaps if you've heard about the Power-On Self-Test (POST), then you may say to yourself, even if there are processes before the MBR, then I don't have to worry about them because they're in ROM, and therefore read-only.

That's not true.

The 16 bytes at the top of memory is where all of UEFI takes place. How is this even possible? UEFI is beyond the scope of this article, but to overview, here is some psuedo-Rust:

pub unsafe fn vtf_main() {
    // There is only enough room for a jump, so imagine 
    // that this is what the top of memory jumps to.

    // Security (SEC)
    sec_main();

    // Pre-EFI Initialization (PEI)
    pei_main();

    // Driver eXecution Environment (DXE)
    dxe_main(); 

    // Tail call Boot Device Selection (BDS)
    bds_main(); 
    unreachable!();
}
If we had the source code to the final stages of the Boot ROM, then it might look something like this pesudo-Rust:
pub unsafe fn rom_main() {
    const SECTOR: usize = 0x200;

    // Pointer to Volume Top File (VTF)
    let vtf = Vec::from_raw_parts(
        0xfffff000 as *mut u8, 8*SECTOR, 8*SECTOR);

    // Copy 8 sectors from SPI flash to memory
    rom::SpiReader::default().read(vtf.as_mut_slice());

    // Tail call the Volume Top File (VTF)
    let vtf_main = 0xfffffff0 as *const Fn();
    vtf_main();
    unreachable!();
}
That seems a bit unfair. The modern UEFI runtime environment gets 8 sectors (about 4 KiB), and the legacy MBR environment gets 1 sector (512 bytes? If I were a bootloader, I would ask for a refund. So let us overview the process:
  • rom_main(), presumably still read-only...
  • vtf_main(), writable (HW)
  • sec_main(), writable (HW)
  • pei_main(), writable (HW, or PEI modules)
  • dxe_main(), writable (HW, or DXE modules)
  • bds_main(), writable (SW, or NVRAM)
  • mbr_main(), writable (SW)
  • vbr_main(), writable (SW)
  • boot_loader()
  • oper_system()
We have certainly skipped a few steps, for example, the Compatibility Support Module (CSM), System Management Mode (SMM), and the Trusted Platform Module (TPM). These three are all critical parts of the startup process, but are too complex to get into detail here. There really is no such thing as "Early Boot" anymore, because what we think of as early, is very late in the process now. Suffice it to say that there is an entire ecosystem of companies and hackers trying to squeeze ever more out of those 16 bytes at the top of memory.

Tuesday, January 27, 2015

Hardware accelerated SHA-1

There are many articles out there describing hardware accelerated AES, which has been out for a few years now, but SHA instructions are not out yet, but should be appearing in ARM and x86 instruction sets sometime this year. So I figure it's time to start talking about them now. The first and formost standard for the SHA-1 algorithm is NIST's current FIPS-180 document, specifically section 6.1.2 "SHA-1 Hash Computation" part 3, which says:

T = ROTL5(a) + ft(b, c, d) + e + Kt + Wt

This formula (which I call the T-formula) is probably the most computationally intensive part of SHA-1, and one of the most surprising things about my research into the ARM NEON and x86 SHA instruction sets is that neither of them implement this formula exactly as is.

SHA-1 Instructions

ARMx86Comment
sha1h aRotate left 30
sha1su0 a, b, csha1msg1 a, bMessage schedule part 1
sha1su1 a, bsha1msg2 a, bMessage schedule part 2
sha1c hash, e, msgsha1rnds4 hash, msg, 0Rounds 0 to 20
sha1p hash, e, msgsha1rnds4 hash, msg, 1Rounds 20 to 40
sha1m hash, e, msgsha1rnds4 hash, msg, 2Rounds 40 to 60
sha1p hash, e, msgsha1rnds4 hash, msg, 3Rounds 60 to 80
sha1nexte hash, msgInvisible e operand

As you can see, ARM and x86 are a bit different. From my research, it seems that ARM omits "Kt" from the T-formula, and x86 omits "e" from the T-formula. These implementation choices imply that SHA-1 optimizations on each architecture will use these instructions very differently. x86 has a publication documenting the SHA instruction set extensions, and if there are any conflicts with this article, then the final word is there. X86 omits "e" in the T-formula, so it must be calculated with the sha1nexte instruction. In pseudo-code, a representative sample of 4 SHA-1 rounds on x86 can be computed by:

work18 = sha1msg2(sha1msg1(work14, work15) ^ work16, work17);
hash19 = sha1rnds4(hash18, sha1nexte(hash17, work18), 3);

ARM on the other hand, has absolutely no documentation about their SHA instruction set, except how to determine if the processor supports it. While this is handy, it does very little to describe the inner workings of each instruction. So the following is just a guess on my part, but given that the inputs to (sha1c, sha1p, and sha1m) are two vectors, there is enough information to compute 4 rounds of SHA-1, just like the x86 sha1rnds4 instruction. ARM omits "Kt" in the T-formula, so it must be added to the work vector. In pseudo-code, a representative sample of 4 SHA-1 rounds on ARM can be computed by:

work18 = sha1su1(sha1su0(work14, work15, work16), work17);
hash19 = sha1p(hash18, sha1h(hash17[0]), work18 + SHA1_K3V);

where SHA1_K3V is a vector of 4 uint32's all of which are SHA1_K3.

For more information see also: sha1.rs

Monday, November 24, 2014

Programming Struggles

Software is hard. Lots of programmers out there are constantly searching for new solutions to old problems, and sometimes they find something that works well. It seems that many recent languages (e.g. Dart, Scala, Groovy, Vala, Hack, Swift) are simply syntax sugar for existing programming languages. Are we ever going to make any progress? I know this question has been rehashed a million times, but I'm going to ask it again anyway.

What makes a good programming language?

  1. Algebraic Data Types - includes sum types (open and closed), and product types (open and closed); too many languages miss these.
  2. Generics, Parameterized Types - includes arrays, vectors, lists, hash tables, trees, parsers, I/O streams, pointers
  3. Reflection, Metaprogramming - includes macros, annotations, doc parsers, syntax highlighting, coverage tools, static analysis tools, serialization, etc.
  4. Memory Pool Management - this is different from memory management, but it refers to the ability to manage the pools themselves, such as the ability to switch between Gc (garbage collected) and Rc (reference counted) memory pool models.

There are probably countless other conditions that I can't think of right now, but if you can think of them for me and leave your comments below, perhaps I will make this article longer.

Algebraic Data Types

What is an algebraic data type? Fundamentally it is a way of combining existing types. For two types, there are two fundamental options for a new type, the new type can store both types (ProductType), or either type (SumType). However, it can get more complicated than that. The way that algebraic data types are implemented in most languages differ by how extensible the resulting types are. I call them open if they can be extended after definition, and closed if they cannot. So to give some examples from existing languages:

  1. closed product types - usually called structures, classes, records, tuples, etc.
  2. closed sum types - usually called enumerations, or tagged unions. Most languages get these wrong because C got them wrong by making them equivalent to integers (which are an example of an open sum type).
  3. open product types - usually called arrays, vectors, or lists. These can always store more data by appending. Adding fields in a subclass can also be an example of this kind of construction.
  4. open sum types - usually called objects, dynamics, variants, etc. These can always be extended by inheritance or by subclassing this type.

There are usually too many ways of performing one of these type definitions. For example Haskell has a Dynamic type, which can be used as a variant type for every type in the language, but it also provides type classes, which also allow open sum type definition.

In Java, and most other OOP programming languages, classes perform most of the above kinds of definitions. Final class fields are an example of closed product type definition. Abstract classes can be used for open sum type definition. Class fields added to a subclass that inherits from a superclass is an example of open product type definition.

Generics

Common generic types include Optional (Maybe in Haskell), List, Array, Pointer, GcPointer (Gc in Rust, ^ in C++/CLI), RcPointer (Rc in Rust, shared_ptr in C++), Box (Box in Rust, unique_ptr in C++), HashTable, etc. Some language don't have generics, but end up expressing concepts with special syntax, for example, * for pointers (in C and Go), and [ ] for arrays (in C and Go).

Reflection

One prerequisite for reflection is that the language must expose the parser used in the compiler (or interpreter, although I cannot think of any language that still uses an interpreter these days). If it does not expose the same parser the compiler uses, then the language must be easy to parse. Too many languages miss this. If people are going to write tools for your language (and they will), then they might not use a fancy parsing framework to help, they might only look for documentation strings or lines that start with an @ symbol. C/C++ is one of the most difficult languages to parse, and yet it makes up most desktop software in use today.

Memory Pool Management

Most languages pick one memory pool model and stick with it. C does not provide any, except for malloc/free, and so reference counting (Rc) emerged as a common solution to this problem. Many scripting languages have opted for garbage collection (Gc) for every single object, which makes it impossible to opt-out. For a general-purpose programming language, it is unacceptable to require that users are forced into one model or another.

Conclusion

Rust has all of these features.

Update: Since the time of this writing, #rust has since informed the author that Gc has been removed from the roadmap, and the standard library. Gc was also not implemented as a garbage-collected pointer, it was more of an auto-reference-counted pointer type.

Saturday, June 8, 2013

Rust wc

So in my attempt to practice writing POSIX command-line tools in Rust, the next tool I tried to rewrite was wc (or word count). This is a simple tool that counts 4 things:

  • lines (separated by U+0D, LINE FEED)
  • words (separated by any whitespace)
  • characters (multibyte encoded, probably UTF-8)
  • bytes

In the interest of brevity, I'm going to refer the reader to this link for the source code. One thing I learned is that the current std::getopts library doesn't have many features, e.g., it doesn't link short options and long options, so you have to check each separately. While I was writing this, some people in #rust on irc.mozilla.org suggested that I look at docopt which has not been ported to Rust yet, still mostly a Python library, but if ported would make a great addition to the language.

Saturday, June 1, 2013

Rust echo

I recently found the Rust programming language, and I've been having a blast of a time learning it. So far, it seems like a mixture of 50 different languages, most notably C++ and Haskell.

To help myself learn it, I decided a good place to start was with command-line utilities, the most basic of which are defined by POSIX. POSIX echo does not have any parameters, it just prints all of its arguments back to the user. BSD echo takes one parameter '-n' and if it's not given, then it prints a newline after all arguments are printed.

This seemed like a good example to learn a language so this was my first attempt:

fn main() {
    let mut newline = true;
    let mut arguments = os::args();
    arguments.shift();
    if arguments[0] == ~"-n" {
        arguments.shift();
        newline = false;
    }
    for arguments.eachi |argi, &argument| {
        print(argument);
        if argi == arguments.len() - 1 {
            if newline {
                print("\n");
            }
        } else {
            print(" ");
        }
    }
}

I talked about this on #rust at irc.mozilla.org and bjz and huonw recommended:

fn main() {
    let args = os::args();
    match args.tail() {
        [~"-n",..strs] => print(str::connect(strs, " ")),
        strs => println(str::connect(strs, " ")),
    }
}

which had some issues with it, so I refactored it to be more functional:

fn unwords(args: &[~str]) -> ~str {
    return str::connect(args, " ");
}
 
fn echo(args: &[~str]) {
    match args.tail() {
        [~"-n", ..strs] => print(unwords(strs)),
        strs => println(unwords(strs)),
    }
}
 
fn main() {
    echo(os::args());
}

which defined a Haskell-like unwords function to help. I am quite pleased with how supportive Rust is to approaching the problem from a procedural paradigm or a functional paradigm. Hooray for multi-paradigm programming!

Review of previous posts

In this post, I'm going to be reviewing my old posts with what I've learned over the past few years. The purpose behind this analysis is to consolidate my ideas into fewer ideas and hopefully come up with some new insights along the way.

This was my first post about XML entity references. The goal was to restrict the DTD grammar to only entities so facilitate usage in other applications that have no concern about elements and attribute lists. Now I would recommend using only DTD since its usage is widespread. I was considering folding this into a macro system I was planning to write, so that one could replace &copy; with &#169; just as easily as it could replace sqrt(4) with 2. I've come to the conclusion that this post was premature, and needs to be included in a more detailed discussion about macro systems, and templating systems such as M4, Genshi, Mustache, and many others.

This was an example of binary parsing, which is still not possible in many languages, but a topic more suited to C structs. GCC has a large number of attributes and packing parameters that accomplish some of the goals of binary parsing, but I still haven't found a good binary parsing framework to date.

This was a bunch of things trying to make Go something that it wasn't. I have since found the Rust programming language, which has all the features I was looking for in this post and more. So, now I would recommend using Rust.

These posts were just crazy comments.

The following posts were all about RDF:

This was a comment primarily intended to allow for arbitrary Common Lisp lists to be represented in RDF.

This was a comment as well, and I still haven't found a datatype URI for this, perhaps something for a future article.

This is interesting because there isn't an XSD datatype associated with large finite-size integers, although you could define it as xsd:integer, then restrict the domain until it matches int128_t. I have since defined a URI for this on my other website.

This was fun. I have since written about these algorithms on my other website.

This is a type of post I'd like to write more of. You know, explaining the pros and cons of certain implementation techniques and why one is better or worse than the other.

This was attempting to illustrate the inconsistencies that I found between different XML standards.

This and this were comments about The Web. I would like to add that I've started to use unqualified identifiers to refer to RDF and OWL core concepts in my private notes:

  • Type = rdfs:Class
    • ObjectType = owl:Class
    • DataType = rdfs:Datatype
  • Property = rdf:Property
    • ObjectProperty = owl:ObjectProperty
    • DataProperty = owl:DataProperty

The following posts were unintentionally about Data URIs:

This was attempting to solve a problem that Data URIs already solve. Now I recommend using Data URIs instead.

This was a good comment, but I don't recommend these namespaces anymore. Now I recommend using Data URIs instead, for example:

<data:text/x-scheme;version=R5RS,vector-length>

This mentions a problem regarding xsd:hexBinary and xsd:base64Binary, which can be solved in several ways:

  1. Candle assigned the type identifier "binary" to the combined value space.
  2. The MediaType "application/octet-stream" is naturally isomorphic to the binary datatype.
  3. The Data URI <data:application/octet-stream,DATA> may be used to represent data of type binary.
  4. The URI
    <http://www.iana.org/assignments/media-types/application/octet-stream>
    may also be used to represent the binary datatype itself.

Sunday, July 29, 2012

On binary search

There are many articles about the pedagogical benefits of writing a binary search algorithm. Instead of just implementing binary search, I wanted to take it one step furtuer. Is is possible to write a standards compliant, formally correct, and beautiful implementation of bsearch() in C?

// This type simplifies the 
// argument list of bsearch.
typedef int (*compare_f)(
  const void *x,
  const void *y);
void *
bsearch(
  const void *key,
  const void *base,
  size_t nel,
  size_t width,
  compare_f compar)
{
  size_t k;
  size_t lo = 0;
  size_t hi = nel - 1;
  const void *mid;

  while (lo <= hi) {
    // If we used (lo + hi)/2, then it
    // would overflow for large integers. 
    // The following formula prevents that.
    k = lo + (hi - lo)/2;
    mid = base + k*width;

    // If we used compar(mid, key), then it 
    // would violate POSIX, which specifies
    // that key must be the first argument.
    int cmp = compar(key, mid);
    if (cmp < 0) hi = k - 1;
    if (cmp > 0) lo = k + 1;
    if (cmp == 0)
      return (void *)mid;
  }

  return NULL;
}

Note that not every argument and variable have been colorized, just the ones that usually give people the hardest time when reasoning about this function. The average (k) can be reduced to (lo + hi)/2 if the array you are handling is less than 263 elements long, but since it isn't technically correct, I didn't write it that way.

Saturday, May 12, 2012

Byte-swap

Today I want to take a brief look at byte swapping from a different point of view. Many algorithms take mathematical concepts and turn them into a step-by-step process, but what if we turn it around, and mathematize a process that originates from computer science?
The idea of mathematizing computer programs is generally done within the context of the design of high-reliability systems and formal methods, which provide a way to prove that a program is correct. Perhaps if we took the time to ensure the correctness of the programs we write, then there wouldn't be so many bugs!

Monday, April 30, 2012

GoLang Highlights

I have been using Go quite frequently lately, and I would like to talk about the experience. There are several introductions to Go floating around, and reactions to its different conventions and ways of doing things than some people are used to. First, I would like to make it clear, though, that I have only used the gc compiler, as it was impossible to install gccgo on Mac OS X at the time of this writing.

What makes Go similar to X?

  • garbage collection
  • interface embedding
  • struct embedding
What makes Go better than X?
  • type syntax (backwards = fowards)
  • typed channels (no serialization)
  • implicit interfaces
  • segmented stacks

Similarities

Every scripting language has some form of garbage collection, so Go is not unique in that respect. Interface embedding is when you used a named interface type instead of a method when defining an interface type. It is similar to Haskell's => when constraining a type class. Struct embedding is when you use a named struct type instead of a field declaration when defining a struct type. This is very similar to inheritance in OOP languages, and it also gives the method sets of those child structs to the parent struct. Both of these are called embedding because they are syntactically similar, you just list the typename. However, they mean different things, and it's important to remember this.

Type Syntax

One of the distinguishing features of Go is its beautiful type syntax. Many programmers have noticed that writing types in C can be a headache, because when you read the type aloud, you have to read backward, or sometimes you have to skip around, back and forth in order to read the type properly. In Go there is no such problem, because the types are written exactly how you would say them in English. While this may be an issue in other languages, most speakers of languages of languages with SVO (Subject-Verb-Object) structure would be happy to see this change. It makes programming in Go a pleasure, and contributed greatly to my own productivity in my Go projects.

Typed Channels

Another Go feature is channels. Go channels are different from most other forms of I/O found in other languages because they are not based on bytes or characters, but data. Typed data. Which means instead of being required to serialize and deserialize your data when communicating between threads, or parts of a larger design, you can send them over a channel as is, knowing that you can use the data immediately instead of validating it first. Serialization is also a big issue with large software projects, since they may be written in many languages, which requires serialization in order to get data from one component to another. Go also has a serialization format, called gob, which can be considered a codec for most major types in the language. So you don't have to serialize, but if you want to, then it's available.

Implicit Interfaces

Another distinguishing feature of Go is implicit interfaces. Most languages with interfaces require explicit statements of implementation, such as Java and Haskell (for type classes). Go has no such requirement (and no such syntax). An interfaces is a collection of methods. A named type has an associated set of methods attached to it, called its method set. A named type implements an interface iff the methods it requires are a subset of the named type's method set. That's it! It's that simple. And because you don't have to make it explicit, it allows you to focus on more important things.

Segmented Stacks

The first and foremost under-appreciated and under-documented feature found only in Go is that of segmented stacks. Segmented stacks is what Go developers call the combination of implementation choices that led to the Go calling convention. It is an intersection of many features, such as: dynamic stack allocation, runtime checks, variable arguments, and deferred functions. Simply put, there is so much that happens between 'return' the statement, and 'RET' the instruction. First, the Go runtime has to check to see if there are any deferred functions to run, then it has to see if it allocated any stack space for the current function, and if it did and it isn't required anymore, then it frees the stack. Similarly, when a function is called, one of the first things that happens is the Go runtime checks to see if more stack space is required. What this means is that - in Go - there are no stack overflows!

Since segmented stacks are such an interesting feature, I suspect that I will revisit them in the future with a more in-depth discussion than I can ever hope to achieve in one paragraph.

Friday, March 2, 2012

Gödel and powerbooleans

What is a boolean?

A boolean is either true or false.

What is a powerboolean?

A powerboolean is a subset of the booleans (B):

  • True = {true}
  • Both = {true, false}
  • False = {false}
  • Null = {}

This set is equivalent to 2B (the powerset of the booleans), hence the name. The powerboolean operations are simply defined as the image of the normal boolean operations over B. The truth tables are as follows:

ANDTBFN
TTBFN
BBBFN
FFFFN
NNNNN
ORTBFN
TTTTN
BTBBN
FTBFN
NNNNN
NOT
TF
BB
FT
NN

These truth tables should not be confused with Belnap logic (which is another 4-valued logic) because there are different definitions for (N AND B), (B AND N), (N OR B), (B OR N). Also, In Belnap logic, B and N can be swapped, and the same rules apply, so they are not unique. In the powerbooleans, B and N cannot be swapped in a similar way, because if you do swap them, the truth tables would have to change. So the powerbooleans have unique values, they aren't just repetitions of some third truth value found in most 3-value logics. The 4-values of the powerbooleans are truely unique.

Who is Gödel?

Gödel is best known for his incompleteness theorems, but he is also known for the fuzzy logic named after him: "Gödel–Dummett" logic (also known as superintuitionistic, intermediate, or minimum t-norm logic). I've talked about product fuzzy logic before, but this time I'd like to talk about Gödel fuzzy logic. The operations are as follows:

  • (x AND y) = min(x, y)
  • (x OR y) = max(x, y)
  • NOT(x) = 1 - x

These operations are defined over a continuous interval from 0 to 1, so I can't really give truth tables for these, but to cut to the chase, they would look very similar to the tables above.

What do they have to do with each other?

If we let NOT(x) = (if x < 0, then x, else 1 - x), extend the interval of Gödel fuzzy logic below zero (which forces OR to be redefined in terms of our new NOT), and assign the following fuzzy values to each powerboolean:

  • True = 1
  • Both = 1/2
  • False = 0
  • Null = -1/2

then Gödel fuzzy logic and powerboolean operations are the same.

Thursday, February 16, 2012

A survey of 'divmod'

Rounding happens, and it happens far more than most people are aware of. As your web browser tries divide a web page (say, 917 pixels across) into 4 equal parts it uses rounding to find how many pixels each column is. Most of the time it is of little importance which direction numbers are rounded, but in some applications, it can be very noticeable. The solution to this problem is a simple calculation involving divmod:

  divmod(917, 4) = (229, 1)

which means a web page which is 917 pixels across can be divided into 4 columns, each of which is at least 229 pixels across, with 1 pixel left over. This concludes our example.

If the numbers being divided are real numbers or integers then there are many, many ways to round the division. If the numbers being divided are positive or unsigned integers, then there are fewer rounding modes (because rtn=rtz and rtp=rta) but there are still many ways. Despite how these modes may seem equivalent for mathematically positive numbers, they are different for fixed machine-size integers. For example, rtn is the same computationally on int32_t and uint32_t, as is rtp, but rtz and rta produce a different computational algorithms on signed and unsigned types. Regardless of the rounding mode chosen, the divmod axiom states:

  divmod(dividend, divisor) = (quotient, remainder)

implies

  dividend = divisor*quotient + remainder

or put in other words, quotient is approximately (dividend/divisor), but which way it is rounded is up to the rounding mode. This is true for every variant in this article except for divmod_euc, which we will discuss later.

This brings us to our analysis of the most common variants, which are usually called truncated division (also called quo-rem), and floored division (also called div-mod). The systems surveyed are: C (POSIX), OpenCL (a variant of C), libMPFR (the R stands for rounding), and Scheme (R5RS, R6RS, and a R7RS draft),

divmod_rtz(a,b)
• "round towards zero"
• 'rtz' suffix (OpenCL)
• x86:idiv (CPU instruction)
• c99:% (C operator)
• c:mod[fl]? (POSIX)
• c:trunc[fl]? (POSIX)
• c:FE_TOWARDZERO (POSIX)
• c:MPFR_RNDZ (libMPFR)
• rnrs:truncate (Scheme)
• r5rs:quotient (Scheme)
• r5rs:remainder (Scheme)
• r7rs:truncate/ (Scheme)
• "rem() has same sign as dividend"

divmod_rtn(a,b)
• "round towards negative infinity"
• 'rtn' suffix (OpenCL)
• c:floor[fl]? (POSIX)
• c:FE_DOWNWARD (POSIX)
• c:MPFR_RNDD (libMPFR)
• rnrs:floor (Scheme)
• r5rs:modulo (Scheme)
• r7rs:floor/ (Scheme)
• "mod() has same sign as divisor"

The problem with these two variants being so common is that it is easy to misuse them. It is quite common to use (A % B) as an index of an array of B elements, without checking if A is positive first. This introduces a buffer overflow error when the program tries to get the -4th element of the array, because that memory address may not be allocated yet, and even worse, if it is allocated then it's probably the wrong data! The core issue with both rtz and rtn rounding modes is that they may give negative remainders. However, there is less possibility of errors with mod_rtn when B is positive, because mod_rtn gives a positive remainder in that case. C99 was the first version of C that actually specified that "%" was mod_rtz, but before the 1999 version, that operator could also be mod_rtn, in which case the operator would be safe. Since C99 standardized on mod_rtz, however, now we know that "%" is unsafe, and therefore, we should always test if A is positive first. Another option would be to use mod_euc, which is discussed later in this article.

The next variant doesn't have a name, but it is a division involving the ceiling() function. Maybe someday we'll have a name for it.

divmod_rtp(a,b) 
• "round towards positive infinity"
• 'rtp' suffix (OpenCL)
• c:ceil[fl]? (POSIX)
• c:FE_UPWARD (POSIX)
• c:MPFR_RNDU (libMPFR)
• rnrs:ceiling (Scheme)
• r7rs:ceiling/ (Scheme)

I couldn't find any implementations of functions for the next two variants, but there is a rounding mode constant in MPFR. Anyways, here they are:

divmod_rta(a,b)
• "round away form zero"
• c:MPFR_RNDA (libMPFR)

divmod_rnz(a,b)
• "round to nearest, ties towards zero"
• (no examples)

There are also some rounding modes used in POSIX (also known as the Single UNIX Specification (the two standards have been synchronized since 2001), what most C programs use) that appear to depend on the environment for which rounding mode to use. The first is completely dependent on the current rounding mode, and the second is basically rn_ (round to nearest), except that ties are rounded according to the current rounding mode. The third seems uncommon in that C is the only language that uses it.

divmod_rtd(a,b)
• "round ... default behavior"
• c:nearbyint[fl]? (POSIX)

divmod_rnd(a,b)
• "round to nearest, ties ... default behavior"
• c:l?l?rint[fl]? (POSIX)

divmod_rna(a,b)
• "round to nearest, ties away from zero"
• c:l?l?round[fl]? (POSIX)

The next variants are vary rare, because they cannot be found in C:

divmod_rnn(a,b)
• "round to nearest, ties towards negative infinity"
• "Round half down" (Wikipedia)
• r6rs:div0, r6rs:mod0 (Scheme)

divmod_rnp(a,b)
• "round to nearest, ties towards positive infinity"
• "Round half up" (Wikipedia)
• elementary school rounding (in the U.S.)

The next variant, known as rte (round ties to even), is probably the fairest variant, in that it has no bias. Most of the variants above have a bias towards positive numbers or negative numbers, or a bias towards zero. The following variant is probably most well-known as being specified by the IEEE-754 floating-point standard. Its claim to fame is that it is unbiased, and it does not introduce any tendancies towards any particular direction.

divmod_rte(a,b)
• "round to nearest, ties to even"
• "Round half to even" (Wikipedia)
• 'rte' suffix (OpenCL)
• c:remquo[fl]? (OpenCL & POSIX)
• c:remainder[fl]? (OpenCL & POSIX)
• c:FE_TONEAREST (POSIX)
• c:MPFR_RNDN (libMPFR)
• r7rs:round/ (Scheme)

The next variant is by far the most advanced divmod on the planet. It is so advanced that it cannot be described by a rounding mode. All of the variants above can be described as div_?(a,b) = round_?(a/b) where the ? are replaced with a rounding mode, but not the next one. Its definition would be div_euc(a,b) = sign(b)*floor(a/abs(b)), but that doesn't even begin to describe it's awesomeness. The reason why div_euc is so amazing is that mod_euc is always nonnegative. Period.

divmod_euc(a,b)
• r6rs:div, r6rs:mod (Scheme)
• r7rs:euclidean/ (Scheme)

I am not the first to notice this. There is a paper titled "The Euclidean definition of the functions div and mod" (from 1992) and R6RS Scheme is also very insistant on this algorithm. In the paper, the author mentions how "definitional engineering" is at fault for the difficulties in using other rounding systems and in particular, fields in computer science such as arithmetic coding, arbitrary precision arithmetic, and programming language foundations all would benefit from (or in my opinion: require) the Euclidean definition, and yet there is no programming language (except for Algol and Scheme) that uses it.

Bibliography

  • OpenCL 1.1 Section 6.2.3.2 Rounding Modes.
  • C99/POSIX <fenv.h> and <math.h>.
  • Revised (5|6|7) Report on Scheme.
  • The libmpfr documentation.
  • The Euclidean definition of the functions div and mod. Raymond Boute. ACM Transactions on Programming Languages and Systems, Vol 14, No. 2, April 1992, Pages 127-144.

Saturday, January 14, 2012

On 'int128_t'

Every programming language has built-in integer types, both signed (representing mathematical integers) and unsigned (representing mathematical nonnegative integers). C compilers usually give these types fancy names like 'unsigned long long', and have a nasty habit of changing the sizes (and meanings) of these types on different platforms. The C type 'short' is usually 16 bits, 'int' either 16 or 32 bits, and 'long' either 32 or 64 bits, depending on platform. This eventually led to the 'stdint.h' header in C99, which provides exact-size types which can be used accross platforms. These are named 'int32_t', 'int64_t', 'uint32_t', 'uint64_t', and so on.

With stuff getting bigger, it's natural to ask the question: "Why 64?" and the answer is generally because the highest integer type most hardware can deal with is 64 bits. Can we go higher? Of course! but how? In this article, I will show you how to define 'int128_t' and 'uint128_t' in C without any compiler hacks. They can be used as parameter types and return types from functions, and they don't require any special memory management or allocation, because they're not pointer types.

First, you might say we could just make an array type:
 typedef int32_t int128_as_int32x4_t[4];
typedef int64_t int128_as_int64x2_t[2];
but which one to we pick? What we really need is a union of each of these, so we can decide later which array type to use. However, neither arrays nor unions can be used as return values from functions, only struct's can be used as return values. So in order to have a type that can be used as a return value we need to make a struct of a union of array types, as follows:
typedef struct int128_s {
union int128_u {
int8_t as_int8[16];
int16_t as_int16[8];
int32_t as_int32[4];
int64_t as_int64[2];
} value;
} int128_t;
and wrap this type in a typedef. But how do we use these new integers? First of all we need some way of constructing 'int128_t's, and in the spirit of 'stdint.h' we can make a 'INT128_C()' macro which expands to a constructed object of type 'int128_t'. We'll need a few functions for this:
int128_t int128_from_int(int from);
int128_t int128_from_str(char *from);
int int_from_int128(int128_t from);
int str_from_int128(char *to, int to_size, int128_t from);
and we can use the second one to define the macro as:
#define INT128_C(x) int128_from_str(#x)
because '(#x)' indicates to the preprocessor to turn x into a string before compile-time, which is then passed to int128_from_str which then returns an object of type 'int128_t'. For compilers that do not support compile-time constant expressions involving function calls, we can also define simpler macros as follows:
#ifdef BIG_ENDIAN
#define INT128_C64(a,b)\
(int128_t){.value = {.as_int64 = {a, b}}}
#define INT128_C32(a,b,c,d)\
(int128_t){.value = {.as_int32 = {a, b, c, d}}}
#else
#define INT128_C64(a,b)\
(int128_t){.value = {.as_int64 = {b, a}}}
#define INT128_C32(a,b,c,d)\
(int128_t){.value = {.as_int32 = {d, c, b, a}}}
#endif
Note that because we use designators (value and as_int##) this part requires a C99 compiler

Conclusion

In order to use this integer type, we also need dozens of other functions, such as add, mul, sub, div, mod, and, or, xor, lsh, rsh, pow, etc., just to match the functionality usually associated with C integer types, and from there the possibilities are endless. A future article could revisit these functions. For now, though, I just wanted to bring focus to this integer type, especially considering how many common-place datatypes fit into an 'int128_t' such as UUID's and IPv6 addresses. We may need this sooner than we think.

Friday, January 28, 2011

Tetrational-point

As you might have guessed, most of my posts are a synthesis of two or more ideas. This time, they are IEEE-754 floating point formats and composite arithmetic formats. To provide some overview, IEEE-754 has been the official standard for representing numbers in computers for over 25 years, and is widely deployed in practically every computer architecture. Composite arithmetic on the other hand is a new approach which uses a combination of integer, rational, scientific, and tetrational representation in unison to achieve greater accuracy, precision, and dynamic range.

References for composite arithmetic include: "Beyond Floating Point" (by C.W.Clenshaw and F.W.J.Olver) which focuses on extending floating point with tetration alone, "Composite Arithmetic, a Proposal for a New Standard" and "Composite Arithmetic, A Storage Form" (both by W. Neville Holmes) which both focus on combining all four approaches, "Design of a Composite Arithmetic Unit for Rational Numbers" (by Tomasz Pinkiewicz, W. Neville Holmes, and Tariq Jamil) which focuses on the implementation of these in hardware, "Design of a 32-bit Arithmetic Unit based on Composite Arithmetic and its Implementation on a Field Programmable Gate Array." (by Tomasz Hubert Pinkiewicz) which focuses on the same, and "Lecture notes on: Computer Arithmetic: Principles, Architectures, and VLSI Design" (by Reto Zimmermann) which seems to be an overview of the subject targeted at students.

The most recent edition of IEEE-754 introduced a 16-bit floating point format, called 'binary16' for short. Since this is the smallest standard floating point format, we will use it as an example of how IEEE-754 represents numbers. To begin we will see how it represents 1.5:

s exponent significand
bin 0 0 1 1 1 1 1 0 0 0 0 0 0 0 0 0
hex 3 E 0 0
value = 1.5

and this is how the standard binary16 format represents infinity:

s exponent significand
bin 0 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0
hex 7 C 0 0
value = Infinity

As you can see, binary16 uses a 5-bit exponent and a 10-bit significand. One important class of representations which do not represent numbers is known collectively as Not-A-Number (NaN) representations. These are when the exponent is all 1's, regardless of the sign. Infinity is a particular example of this, since the exponent is all 1's and the significand is all 0's. If the significand is nonzero, then the remaining representations fall into two categories: signaling NaNs (sNaN) and quiet NaNs (qNaN). A signaling NaN is traditionally represented with a 0 in the MSB of the significand, and some other bit nonzero. A quiet NaN is traditionally represented with a 1 in the MSB of the significand, with anything in the other bits. Since sNaNs can cause errors and trap handlers to invoke, it is a bad idea to use them to represent numbers, since many architectures will automatically convert sNaNs to qNaNs. So, we will leave sNaNs alone, and replace qNaNs with a tetrational number representation format.

This tetrational format will use the two MSBs to represent quietness (q) and height (h) respectively. The tetrand will become the exponent at the top of the exponential tower. So a height of 0 will indicate a value of 2^2^2^2^2^(0.tetrand), and a height of 1 will indicate a value of 2^2^2^2^2^2^(0.tetrand). In general, the value associated with the tetrational format is:

(-1)sexp2h+5(0.tetrand) -- HTML
-1 s exp 2 h + 5 0.tetrand -- MathML

We will start at 65536, which is just greater than the largest value represented by binary16 (65504). Extending this to larger formats (like binary32) should probably start here as well, because the next height is too much larger than the largest number represented by binary32. Starting from 65536, we can represent it as follows:

binary16: s exponent significand
tetra16: s exponent q h tetrand
bin 0 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0
hex 7 E 0 0
value ≈ 65536

We get this value because 2^2^2^2^2^(0.0) = 2^2^2^2^1 = 2^2^2^2 = 2^2^4 = 2^16 = 65536. The next greater representation is

binary16: s exponent significand
tetra16: s exponent q h tetrand
bin 0 1 1 1 1 1 1 0 0 0 0 0 0 0 0 1
hex 7 E 0 1
value ≈ 71036

and perhaps another interesting representation is that of googolplex:

binary16: s exponent significand
tetra16: s exponent q h tetrand
bin 0 1 1 1 1 1 1 1 1 0 1 1 0 0 1 0
hex 7 F B 2
value ≈ googolplex = 10^(10^100)

Note that we only used a single bit for the height of the exponential tower, but we could have used more. I think that 2 bits should be used for height in binary32, and 3 bits should be used for height in binary64 and binary128. All floating point formats would encode the height and tetrand in the extra bits of a qNaN so as to avoid conflicts with hardware arithmetic.

Here is an overview of the extensions to the binary16 format:

#x0000 = +0
#x0001 = 5.960464477539063e-8
... (standard binary16 floating-point) ...
#x7BFF = 65504.
#x7C00 = +Infinity
#x7C01 = +sNaN
... (standard binary16 signaling NaN) ...
#x7DFF = +sNaN
#x7E00 = 65536 = 2^2^2^2^2^(0)
#x7E01 = 71036.
... (new tetrational-point) ...
#x7E?? = 2^2^2^2^2^(0.??_16)
...
#x7EFE = 8.9423389054e+15721
#x7EFF = 6.2622606021e+17594
#x7F00 = 2^65536 = 2^2^2^2^2^2^(0) = 2^2^2^2^2
#x7F01 = 6.8504792165e+21383
... (new tetrational-point) ...
#x7F?? = 2^2^2^2^2^2^(0.??_16)
...
#x7FFE = 10^(2.69191e+15721) which is < 2^(2^65536)
#x7FFF = +qNaN (unique)
#x8000 = -0
#x8001 = -5.960464477539063e-8
... (standard binary16 floating-point) ...
#xFBFF = -65504.
#xFC00 = -Infinity
#xFC01 = -sNaN
... (standard binary16 signaling NaN) ...
#xFDFF = -sNaN
#xFE00 = -65536
#xFE01 = -71036.
... (new tetrational-point) ...
#xFE?? = -2^2^2^2^2^(0.??_16)
...
#xFEFE = -8.9423389054e+15721
#xFEFF = -6.2622606021e+17594
#xFF00 = -2^65536
#xFF01 = -6.8504792165e+21383
... (new tetrational-point) ...
#xFF?? = -2^2^2^2^2^2^(0.??_16)
...
#xFFFE = -10^(2.69191e+15721) which is > -2^(2^65536)
#xFFFF = -qNaN (unique)

In conclusion, we have shown exactly how to increase the dynamic range (quite dramatically) of standard IEEE-754 floating point formats by using a tetrational representation encoded in the unused bits of quiet NaNs. This provides a backwards-compatible hardware-accelerated tetrational floating point format which will encode approximate overflows as precisely as possible. So instead of overflows, you get a number! This makes it possible to be more informative when it comes to mathematical functions such as exp, log, or 1/x. With a bigger dynamic range, many of the outputs of these functions will no longer be indeterminate, but instead, finite.

Monday, January 3, 2011

GoLang Proposals

Go is a very new programming language, barely a year old. Since its initial release, it has become an instant success, almost overnight. Google designed Go primarily for high-level systems programming. Since then, Google has replaced a few of its internal servers with custom Go servers. I believe this success is driven by the simplicity of Go, and its special features which focus on concurrency.

Compared to C, it has many more features so I consider its features a strict superset of C's features. Compared to C++ however, Go is very different, possibly described by a classic Venn-diagram in which Go's features and C++'s features intersect, but both have features the other doesn't. For example, method overloading can be accomplished in both languages, but in different ways.One feature that C++ has enjoyed is that of templates, which do not exist in Go. This requires that algorithms that can work on multiple datatypes be written out each time, which negatively affects readability and maintainability. While adding full-fledged C++ templates to Go is certainly possible, I believe we need to look at other languages for guidance.

Java started out as a simple language. Java's success is due in part to this simplicity, however, language designers at Sun (now Oracle) decided very late in the process (2004, a full 9 years after its introduction in 1995) to add generics to the language. In addition, they chose to use C++ syntax, which has conflicts with the shift operator (>>). In my opinion, avoiding the introduction of generics has divided the community, effected incompatibilities, and complicated the standard library with multiple versions of the same function.

Depending on your definition of generics, they may have different kinds of parameters. The most intellectually challenging are Agda generics, which imply that generics are just functions which return types, and any function may take types as a parameter. C++ templates are similar to this kind of generics, but make a distinction between usual functions and functions returning types. Since this is a minority when it comes to generics implementations, we will not consider this kind of generics in the remainder of this article.

The most prevalent form of generics are found in Java and Haskell, which only allow types as parameters. From this point of view, the only generic type found in Go today is the map type, so it will be the primary example used.


Proposal #1: Add first-class types

This is doomed for failure, because it requires Agda-like (or C++/RTTI) semantics, but it is worth discussing. It is the proposal which requires the least syntactic changes, even if it might be the most intellectually demanding on semantics and runtime. If Go did have first-class types, then we could define the Map type as follows:

func Map(KeyT .(type), T .(type)) .(type) {
type mapT []struct{
hash int
key KeyT
value T
}
return mapT
}

Using this proposal, we can simulate map[KeyT]T with Map(KeyT, T). As you can see, type parameters are indicated with the ".(type)" token as is found in Go type-switch statements. One advantage of this system would be that it could be used to define Array(n, T) and Matrix(m, n, T) which would be impossible to define using more restrictive generics. Other functions that could defined using this proposal are new and make, which take a type as their first parameter. Here is a summary of the related changes to the Go specification:

Result        |= ".(type)"
ParameterDecl |= TypeName ".(type)"
Expression |= TypeName

In addition to these syntactic changes, a few paragraphs of rules and discussion maybe required.


Proposal #2: Add 'generic' (or '_Generic') keyword

This is the crux of this blog article. Since first-class types require that types and objects be intermixed, it requires that every parameter be suffixed with .(type), but with the generic keyword, we can enforce that every parameter be a type parameter (Java/Haskell-style generics). This makes declarations much easier to read, and has provides a much more structured style.

generic Map[KeyT]T []struct{
hash int
key KeyT
value T
}

Using this proposal, we can simulate map[KeyT]T with Map[KeyT]T. As you can see, this is very close to the standard built-in map type, with the obvious case difference. Here is an overview of the associated changes to the Go specification:

TypeLit       |= GenericType
TopLevelDecl |= GenericDecl
GenericType = "generic" GSignature Type
GenericDecl = "generic" identifier GSignature Type
GSignature = GParameters identifier
GParameters = "[" [ GParameterList [ "," ] ] "]"
GParameterList = GParameterDecl { "," GParameterDecl }
GParameterDecl = identifier

Having considered adding generic syntax to LiteralType, I don't think it would work well with literal syntax or semantics. What would such a literal mean? Would it simply be syntactic sugar for the base-type of the generic type with all the free variables filled in? If all that would be gained is syntax sugar, then I don't see the value of adding it to literal syntax.

This generics model also allows more than one parameter inside the brackets, but it also requires at least one parameter after the brackets, which is also very similar to how generic types work in Haskell, since the prevalence of monads has made the last parameter more important than the others. This generics model is also much closer to Go's existing map type, the only problem now is that the first letter is uppercase "M" whereas the built-in map type has a lowercase "m". This leads us to our next section, which discusses another proposal.


Proposal #3: Add 'attrib' (or '_Attrib') keyword

This proposal combines two issues into a single solution. The first issue is nonessential attributes (such as alignment, packing, etc.) for which GCC uses the __attribute__((id)) syntax. The second issue is the public/private convention in Go which may be an issue in the future if it is used to implement existing APIs such as POSIX, which require lowercase external symbols. The solution I propose to both of these issues are a new syntax: _Attrib(id), which allows simple annotation of declarations in such a way as to override normal Go semantics. Consider two such attributes: public and private, which would allow us to define the built-in map type as follows:

generic attrib(public) map[KeyT]T []struct{
hash int
key KeyT
value T
}

Using this proposal, there would be no difference between this defined type and the built-in map type. This would allow very small Go compilers which need only handle arrays and slices, and leave the map type, and possibly other built-in functions such as cap and len to a library implementation.

The syntax was designed to force the attrib specifier immediately before the identifier, to make it clear which identifier it is referring to. The changes to the MethodDecl production are questionable, and require more consideration and discussion. I have also considered moving AttribSpec to before the keywords which would simplify the grammar significantly. This is the summary of all the required extensions to the Go specification.

ConstSpec    |= AttribSpec IdentifierList 
[ [ Type ] "=" ExpressionList ]
TypeSpec |= AttribSpec identifier Type
VarSpec |= AttribSpec IdentifierList
( Type [ "=" ExpressionList ]
| "=" ExpressionList )
MethodDecl |= "func" Receiver AttribSpec
MethodName Signature [ Body ]
FunctionDecl |= "func" AttribSpec identifier
Signature [ Body ]
GenericDecl |= "generic" AttribSpec identifier
GSignature Type
AttribSpec = "attrib" "(" identifier
[ "(" ExpressionList ")" ] ")"

Proposal #4: Add 'pragma' (or '_Pragma') keyword

Another method used for nonessential attributes are top-level pragmas. Using pragmas, you can specify information to be used on a per-file or per-package basis. This could be a useful and simple extension to the language that could bring existing GCC syntax to Go compilers. Here is an example of using pragmas in a Go source file:

pragma("GCC poison strdup")
pragma("DSGO prefix pthread_")

Other possibilities for pragmas are to use Go with OpenMP, even though it might sound funny at first, because most of the features of OpenMP are already in Go! However, we should never underestimate what people will do with compilers, given the chance. Here is a summary of the associated extensions to the Go specification:

TopLevelDecl |= PragmaDecl
PragmaDecl = "pragma" "(" string_lit ")"

Conclusion

We have reviewed four possible extensions to the Go programming language, which may not seem in the spirit of Go right now, but over time may become necessary. If Go is to be used for serious projects, developers may come to expect these features, rather than hope for them. As we have learned from Java, if we wait for too long to introduce new features to Go, it may polarize the community, which would be an undesirable outcome for everyone.

Sunday, August 16, 2009

Fuzzy Scrollbars

After several weeks away from this blog, I would like to start up again with the exciting field of fuzzy logic. Most introductions to fuzzy logic talk about how everything is different, and overview the three major types: Gödel, product, and Lukasiewicz, but I'm going to take a different approach. I'm going to stick with product fuzzy logic for the remainder of this post.

Product Fuzzy Logic

It is important to discuss domains when regarding operations, and fuzzy logic is somewhat consistent in its use of the domain [0, 1], which includes 0, 1 and all the real numbers in between. Let's call this the Clamp datatype. There are obvious mappings to and from Clamp and Bool: 0 maps to False, 1 maps to True, and the in betweens can be handled on a case-by-case basis (possibly to throw and error or something).

The operations on the Clamp datatype would be both arithmetic and logic operations, which would include addition, subtraction, multiplication, division, conjunction (and), and disjuction (or). How would these be defined in product fuzzy logic? According to the product T-norm (the fancy name for how "and" is defined), the logic operations would be defined as

  • not(x) = 1 - x
  • and(x, y) = xy
  • or(x, y) = x + y - xy
  • implies(x, y) = 1 - x(1 - y)

One interesting thing about product fuzzy logic is that there are multiple ways of defining equiv, depending on what properties you are looking for. One possibility is that equiv(x, y) = and(implies(x, y), implies(y, x)), which would mean that it would have to be defined as equiv(x, y) = (1 - x - y + 3xy - yx2 - xy2 + x2y2). This is quite a mess, and the same effect (in terms of mapping to Bool) can be accomplished by the definition equiv(x, y) = (1 - x - y + 2xy).

Other considerations for operations on the Clamp datatype are that when performing arithmetic operations, the output of the normal operation must be minimized or maximized to fall within the range [0, 1]. This may have the unfortunate side effect of many run-time errors, or unexpected mathematical outputs. If all conversions to and from the Clamp datatype are excplicit, then this is not a problem.

Scrollbars

Just as the String is the basis for the text field widget, the Clamp datatype can be thought of as the basis for the scrollbar widget. Scrollbars are usually tied to a viewport, which can contain an image, a document, or a webpage. Usually the viewport has all the information that the scrollbar needs to do its job. It has height and width information of the entire image, and the height and width information of the part that is showing. Using this information, the only additional information that needs to be stored in the scrollbar is the percentage of how far down on the scrollbar you are, which is equivalent to what the Clamp datatype provides.

Colors

Why is it that such a fundamental datatype like Clamp is not found in more programming languages? Usually it is defined as equivalent to Float or Double, and intended to be used with values between 0.0 and 1.0, but no strict validation or bounds checking are done to ensure that this is so. One example where this kind of usage is found is in OpenGL, which we will talk about next.In OpenGL, one of the datatypes that is pervasive throughout the API is the GLclamp datatype. This is also a value between 0 and 1, and also where I got the name from. With this datatype, OpenGL defines some texture and image datatypes as structure with members of this type, for example a color in OpenGL is a structure with 3 GLclamps.

With so many applications, the Clamp datatype is a prime example of a design goal that I've been trying to find a name for. When abstractions are too low-level, then you may have few abstractions (like bit and byte), but using these abstractions is a nightmare (just look at assembly code). When abstractions are too high-level, then you may be able to use them easily (like drag-and-drop database form designers), but the number of abstractions makes a steep learning curve, and a high pressure on the discovery of abstractions other people have made (for example: some game libraries have 3 types of fog, and dozens of types of meshes, depending on whether you want to add skeletons or water ripples, etc.). The design goal I'm trying to acheive is a balance of these two extremes that balances low-level abstraction and high-level abstraction in such a way that there are few abstractions, and using them is easy. I think Clamp datatype meets both of these requirements.

From fuzzy logic to colors to scollbars, Clamp seems to form the basis of an unrecognized basis for computation that may be useful in the future.

Monday, June 15, 2009

Embedded OpenGL

OpenGL is a graphics library that has provided a foundation for many other high-level graphics libraries built on top of it. Because it is so versitle and low-level, it has been proven to stand the test of time, and has evolved to handle the changing demands that library authors have been placed on it. As demands have changed, new functions are added, and one trend between all versions of OpenGL is that more specific functions are being replaced by more general functions. For example, the functions glColorPointer, glNormalPointer, and glTexCoordPointer have been augmented with glVertexAttribPointer, which can be used to replace all of the previous functions. This has led to a completely different view of OpenGL that has led to the development of smaller profile of OpenGL functions called the Embedded Specification, or OpenGL-ES for short. One side effect of this is that the more abstract functions are increasingly similar to an object-oriented library, which means there are fewer and fewer functions that do not fit into an object model of OpenGL. If we partition the functions found in OpenGL-ES 2.0 into an object-oriented hierarchy, then we arrive at the following classes:

  • Blend
  • Buffer -- pixel buffers, vertex buffers, and index buffers
  • Capability -- abstract class with glEnable and glDisable
  • Clear -- clearing the background to a solid color
  • Color
  • CullFace -- consists of glCullFace and glFrontFace
  • Depth
  • FrameBuffer
  • Get -- reading global state with functions like glGetString etc.
  • Mipmap -- utilities for the Texture class
  • Program
  • RenderBuffer
  • Shader -- vertex and fragment shaders
  • Stencil
  • Texture
  • Uniform -- uniform variables
  • VertexAttrib -- vertex attributes

after removing these functions from the OpenGL-ES 2.0 API, we are left with the miscellaneous functions:

  • glDrawArrays(mode, first, count)
  • glDrawElements(mode, count, type, indices)
  • glFinish()
  • glFlush()
  • glLineWidth(width)
  • glPolygonOffset(factor, units)
  • glSampleCoverage(value, invert)
  • glPixelStorei(name, param)
  • glReadPixels(x, y, width, height, format, type, pixels)
  • glScissor(x, y, width, height)
  • glViewport(x, y, width, height)

This says a lot about OpenGL-ES, especially the fact that glDrawPixels has been removed. This means the only way to copy pixels into an OpenGL-ES context is to apply a texture with the pixel data to a rectangle. However, OpenGL-ES forbids GL_QUADS, so you have to use GL_TRIANGLE_FAN to accomplish the same result. glReadPixels, glScissor, and glViewport can be related in that they all accept a rectangle as an argument.

Although OpenGL-ES allows one to do everything that can be done in OpenGL 2.0, there is more of a suggestion to use more general interfaces, like shaders and buffer objects, since all of the fixed functionality has been removed. This means that it is possible to reimplement all of OpenGL in OpenGL-ES. However, since ES is still rather new, there have not been any such implementations of fixed functionality in terms of shaders or the like. There are some tutorials that show how to duplicate such simple functionality as colors in terms of shaders, but a complete reimplementation is still nowhere to be found.

Monday, June 8, 2009

Semantic MathML

I don't know what they were thinking, but there is a much better way to encode MathML in RDF. One can be tempted to assign an RDF property to every element in MathML, but that wouldn't be the OpenMath way. Since MathML3 is getting more and more dependent on OpenMath, it seems appropriate to encode as much as possible using this combined system. MathML3 is split up into several sections: Presentation, Pragmatic Content, and Strict Content. The last one requires only 10 XML Elements to be understood by MathML3 processors, namely: m:apply, m:bind, m:bvar, m:csymbol, m:ci, m:cn, m:cs, m:share, m:semantics, m:cerror, and m:cbytes. This provides for a great economy of thought, and a chance to easily define a total mapping from MathML to RDF. MathML3 already defines a mapping from MathML2 to Strict Content MathML3, so this is the only set of Elements we need to consider. These are the prefixes we will use:

@prefix ari: <http://www.openmath.org/cd/arith1#> .
@prefix fns: <http://www.openmath.org/cd/fns1#> .
@prefix sts: <http://www.openmath.org/cd/sts#> .
@prefix sm: <http://example.com/SemanticMath/> .
@prefix m: <http://www.w3.org/1998/Math/MathML> .

These are the rdfs:Class's we will define:

  • sm:Content (all MathML Content)
  • sm:Number (for <cn/>, subclass of sts:NumericalValue)
  • sm:String (for <cs/>, subclass of xs:string)
and these are the rdf:Property's we will define:
  • sm:apply :: Property * sm:Content
  • sm:applyTo :: Property * (List sm:Content)
  • sm:bind :: Property * (List sm:Content)
  • sm:bindOp :: Property * sm:Content
  • sm:bindIn :: Property * sm:Content
  • sm:error :: Property sm:Error sm:Content
  • sm:errorWas :: Property sm:Error (List sm:Content)
Strict Content MathML3 can be translated with the following algorithm:
  • <m:cn> NUM </m:cn>
  • "NUM"^^sm:Number
  • <m:cs> TEXT </m:cs>
  • " TEXT "^^sm:String
  • <m:ci> Name </m:ci>
  • Use blank node identifier (like _:Name)
  • <m:csymbol> Symbol </m:csymbol>
  • Use URIs (http://www.openmath.org/cd/arith1#plus for <m:csymbol cd="arith1">plus</m:csymbol>, or the value of the definitionURL for MathML2)
  • <m:share/>
  • Use URIs
  • <m:cbytes> DATA </m:cbytes>
  • " DATA "^^xs:base64Binary
  • <m:cerror> Symbol Content* </m:cerror>
  • [] rdf:type sm:Error ;
    sm:error Symbol ;
    sm:errorWas LIST(Content) .
  • <m:apply> Symbol Content* </m:apply>
  • [] sm:apply Symbol ;
    sm:applyTo LIST(Content) .
  • <m:bind> Symbol Vars* Content* </m:bind>
  • [] sm:bindOp Symbol ;
    sm:bind LIST(Vars) ;
    sm:bindIn LIST(Content) .

This allows all of Strict Content MathML to be encoded in a set of RDF triples. In order to have the full semantics of MathML and OpenMath there would have to be an OM-interpretation, OM-entailment, and so on, just as there is with every other application of RDF. Although I have not worked out the details of this, an OM-interpretation of an RDF graph that contains these properties would have to treat sts:mapsTo special, and maybe map it to an appropriate rdfs:Class. As an example of how this might be used, this is how the Haskell code (\x -> x + 2) would be translated into RDF using the properties listed above as follows:

_:f sm:bindOp fns:lambda ;
sm:bind LIST(_:x) ;
sm:bindIn LIST(
[ sm:apply ari:plus ;
sm:applyTo LIST(_:x "2"^^sm:Number) ]).

Now that RDF has lambdas, the possibilities are endless!

Sunday, June 7, 2009

Subtly Different Linked Lists

If you don't know what a linked list is, then you probably shouldn't be reading this. However, the idea is very simple: a linked list is a list constructed from smaller lists of length two. So a list of length 3 would be constructed as (A, B, C) = (A, (B, (C, Nothing))), where Nothing would indicate that we have reached the end of the list. Most programming languages have some kind of linked list datatype. C++ has the list<T> type in STL, Lisp has the 'list type, Haskell has the [T] type, and RDF has the rdf:List type. We will not consider sequences here, so C types will be left for a future article.

Without any type restrictions, such a pair need not be required to make a list. For example, we could make the structure ((A, B), C), but we could not interpret it as a list. This is exactly how the Common Lisp cl:cons constructor works. The Common Lisp HyperSpec calls anything constructed with cl:cons a list. The special case where the second element of cl:cons is either another cl:cons or a cl:nil is called a proper list. This restriction makes a subtype which can always be interpreted as a list. This subtype corresponds to the lists found in scripting languages such as Perl, Python, and Ruby. RDF lists can also be described as proper lists, since an RDFS-interpretation requires that the range of rdf:rest is rdf:List, so any attempt to make an improper list with rdf:rest will result in an inconsistent RDF graph.

With the restriction that the second part of a pair is a list, we obtain so-called heterogeneous lists, because the members of the list (encoded as the first part of each pair) can be of any type. If we also enforce the restriction that each member of the list is of the same type, then we obtain what is called homogeneous lists for obvious reasons. This subtype is what is found in more strict languages, such as list<T> in C++ and [T] in Haskell. This is an overview of the different kinds of lists we have talked about:

  • Improper list (a, (b, c))
  • Proper list (a, (b, (c, ())))
    • Heterogeneous list [1, "message"]
    • Homogeneous list [1, 2, 3]

While we have talked about lists before, restricting RDF's heterogeneous lists to obtain homogeneous lists. However, in this article we are going to consider generalizing RDF's proper lists to obtain improper lists as well. In order to do this, we will make a distinction between cl:Cons the Class and cl:cons the constructor. First we need rdf:List rdf:subClassOf cl:Cons so that they can work together, and then cl:Cons will represent both improper lists and proper lists, and rdf:List will represent proper lists only. To represent an improper list, would would have to be an instance of cl:Cons but not rdf:List. For a heterogeneous list, one would have to be missing the ex:listType property, and for a homogeneous list, one would require the presence of the ex:listType property. This covers all the different list types discussed above.

Saturday, June 6, 2009

HTML Script Datatypes

While wandering through the W3C specification for rdf:text, I realized a similar notation could be used for script objects, but there were too many pieces missing from the standards to connect everything together. So I will try to connect these ideas as best I can. HTML 4.01 defines multiple datatypes, some of which are redefined by CSS, such as Color and Length. The one that cought my attention was Script, which could be used to represent scripts written in different languages.

UNIX has long since had this datatype built in to almost every Shell interpreter. The so-called Hash-Bang system allows you to have programs written in any language that do not need to be compiled, but are passed to the appropriate program for interpretation. While RDF has two types that are very similar, it doesn't have a type for scripts or functions. Using a mixture of JavaScript and HTML datatypes, we can express a function definition in RDF as follows:

 @prefix hdt: <http://www.w3.org/TR/html4/types.html> .
@prefix js: <urn:ecmascript:> .

_:f rdf:type js:Function .
_:f js:Call """
function () {
this.done = true;
} @application/ecmascript"""^^hdt:Script .

Here you can see the @ notation is used to separate the script data from the media type, just as it is in the rdf:text datatype. This would allow a mixture of data (RDF graphs) and code (JS scripts), which are the beginnings of a full-fledged programming language.