In the pictured single-cycle MIPS architecture, RegDst selects the write address of the register file: zero selects instruction bits , the rt field used by a load; one selects bits , the rd field used by an MIPS R-type instruction. RegWrite enables the clocked write to the selected processor register.
ALUSrc selects the second ALU input: zero uses the second register file read output, while one uses the sign-extended 16-bit immediate field. The zero signal is produced by the ALU, rather than being an opcode-decoder output: it is one exactly when the ALU result is zero. During beq, the ALU subtracts its operands, so zero tests equality. The diagram combines it with Branch to select the branch target instead of .
The custom CMOS circuit uses eight NMOS transistors and eight PMOS transistors in the output networks, plus eight transistors in the four input CMOS inverters:
The complementary types give an actively driven output at either supply rail. An NMOS transistor provides a strong connection to ground; a PMOS transistor provides a strong connection to . An NMOS transistor used alone to pass a high level loses gate overdrive as the output rises, giving a threshold-degraded high level; the complementary problem affects a PMOS transistor passing a low level. Complementary networks avoid these degraded levels and, ideally, have no direct supply-to-ground conducting path for a stable input. Physical leakage and switching current remain, but a resistive load and its continuous static current are unnecessary.
A more economical static CMOS design follows from factoring the condition for . The output is low exactly when both input pairs disagree:
Generate with four CMOS inverters. Write or for an NMOS transistor or PMOS transistor whose gate receives . In the following network expressions, a dash means a series connection and means a parallel connection.
Connect this eight-device pull-down network between and ground:
The first parallel module conducts exactly when and the second exactly when . Their series connection therefore conducts precisely when should be zero.
By complementary CMOS network duality, connect this eight-device pull-up network between and :
A PMOS transistor conducts when its gate is zero. Consequently the first upper branch conducts when , namely ; the second conducts when . Their parallel connection gives the required logical disjunction. For every stable binary input, exactly one of the pull-up network and pull-down network conducts.
Figure 1.
Complementary CMOS networks: eight PMOS transistors above the output and eight NMOS transistors below it
. Details.
The figure labels each MOSFET channel by its gate signal; the four input CMOS inverters are additional to the devices drawn.
Each parenthesized pair in the given Boolean function tests equality of two input bits:
This is logical equivalence, or the two-input XNOR gate function. Thus is one whenever either input pair agrees. Generate the four complemented inputs with four inverters, and define the following NAND gate outputs:
Feed these four outputs into a single four-input NAND gate. De Morgan's laws give
Here juxtaposition denotes logical conjunction. The construction uses only inverters, two-input NAND gates and one four-input NAND gate, as required.
For a CMOS inverter, connect one PMOS transistor from the positive supply to the output and one NMOS transistor from the output to ground; drive both gates with the input . When , the PMOS transistor conducts and the NMOS transistor is off; when , the reverse holds. Thus the output is , using two transistors.
For a four-input NAND gate, use four NMOS transistors in series between the output and ground, with gates . Use four PMOS transistors in parallel between and the output, with the same gate signals. The pull-down network conducts exactly when all four inputs are one; the pull-up network conducts whenever at least one input is zero. Hence
As usual for static CMOS, tie the NMOS transistor bodies to ground and the PMOS transistor bodies to .
A file descriptor table belongs to a process: its small nonnegative integer indices are file descriptors, and each occupied entry points to an open file description. It also holds descriptor-specific information, such as whether the descriptor closes on a successful program replacement. There is one logical file descriptor table per process in the traditional model; implementations can let processes explicitly share one.
The system-wide open file table, called the file pointer table here, contains open file descriptions. Each entry records an opening's access mode, current file offset, reference count and reference to an in-memory inode. Duplicated file descriptors, including inherited ones, can refer to the same open file description and therefore share its file offset. Independent opens of the same file normally create separate open file descriptions and separate offsets. There is one logical system-wide open file table, with many entries.
The system-wide in-memory inode table caches the inodes currently needed, including shared file metadata, locking and reference information. Its identity key must include the file system as well as the inode number when several volumes are mounted. There is one logical system-wide active inode table, distinct from the persistent inode table stored on each volume. These are logical organizations, not requirements for a single contiguous array in memory.
The hard link count in an inode duplicates information recoverable by counting directory entries that refer to its inode number. Keeping the count makes the last-name test fast: a hard link removal need not search every file-system directory. A file-system consistency checker can independently recount the references and compare the result with the stored value.
Likewise, a block allocation bitmap summarizes ownership information recoverable from allocated inodes, their file data blocks, their indirect block pointers, and reserved file system structures. This summary makes allocation efficient and supplies an independent consistency check against multiply allocated or lost blocks. Replicated superblock information is another possible form of data redundancy, helping recovery if one copy is damaged. These examples have distinct benefits: a cached count or allocation summary saves work, while an independent or replicated representation helps detect or repair corruption. Consistent updating is essential; redundancy alone does not make a file system crash-safe.
Use a block allocation bitmap, one bit per allocatable disk block number, or a free-block list. Allocation removes a free block from the free representation; deallocation inserts it. Reserved file system structures, including the inode table and the free-space representation itself, must never be offered as free file data blocks. Free inodes can be tracked separately by an inode allocation bitmap.
A file data block becomes reusable when the file system has removed its ownership from the file. This happens, for example, when truncation discards that portion of the file, or when the file is finally destroyed. Destruction requires both removal of its last hard link and release of its last live reference: unlinking an open file must not reclaim blocks still needed through an open file description. A memory-mapped file, if supported, also requires its references to be accounted for. The indirect block pointers' own blocks are reclaimed when they are no longer needed.
There are two relevant notions of accessibility. Pathname-accessible files are those reached from the root through valid directory entries, subject to permissions; their allocated blocks are found through their inodes. Already-open files can additionally remain accessible through file descriptors even after their last names disappear. Allocated file system structures and blocks pinned by an operation must also be excluded from allocation. A block allocation bitmap must therefore describe actual allocation, rather than merely the blocks of currently named regular files.
One processor can still run several processes and be interrupted. Updating ownership and the free-space representation must therefore be serialized with respect to competing operations, for example through an appropriate kernel critical section. Persistence across a crash additionally requires file-system crash consistency, such as ordered updates or file-system journaling.
The file-system namespace is rooted at the root file-system directory. Each directory entry associates a local name with an inode number. To resolve a pathname, start at the root for an absolute pathname, or at the process's current file-system directory for a relative pathname, and look up successive components in successive file-system directories. A file-system directory is itself a file with a special interpretation for its data.
The inode table stores file metadata and the information locating contents. The file-system directory structure supplies names and the connections used in pathname lookup. Thus the file is the object represented by its inode, whereas a file name is one entry referring to that object. Two hard links can give the same file two names, possibly in different file-system directories. Renaming a file need not move its contents or change its inode number; after unlinking an open file, a file can temporarily have no names and still be accessible through an existing file descriptor.
In the traditional Unix file system model, a regular file consists of file metadata in an inode and an ordered sequence of bytes stored in file data blocks. The inode records the type, owner, permissions, length, timestamps and hard link count, together with the information locating its file data blocks. A small file can use direct file data block addresses; a larger file can additionally use indirect block pointers, whose blocks contain further addresses rather than user data. The final file data block need not be completely occupied.
An inode number identifies an entry in the inode table of this file system; it identifies the file, rather than a particular piece of its contents. A disk block number identifies a fixed-size storage region on the volume. Resolving an inode number first obtains the inode; following its direct or indirect block pointers then finds the required file data block. The two numbers belong to different address spaces, even though the inode table itself occupies disk blocks.
Store the minimal elements of the finite generating set. The represented upper set itself may be infinite; the code manipulates its finite basis.
class UpSet[T <: Ordered[T]](c: Set[T]) extends Ordered[UpSet[T]] {
  private val basis: Set[T] = minimal(c)
  def contains(x: T): Boolean = basis.exists(a => a <= x)
  def intersection(that: UpSet[T]): UpSet[T] = {
    val generators: Set[T] =
      for (a <- basis; b <- that.basis) yield a.lub(b)
    new UpSet[T](generators)
  }
  def <=(that: UpSet[T]): Boolean = basis.forall(a => that.contains(a))
  def lub(that: UpSet[T]): UpSet[T] =
    new UpSet[T](basis union that.basis)
}
The abstraction function and representation invariant are
Removing nonminimal generators preserves the upward closure: in a finite generating set, repeatedly descending below any discarded element must reach a retained minimal element. Thus the constructor represents exactly the required finitely generated upper set.
Membership follows directly from the abstraction function. For intersection, an element lies above some and some exactly when it lies above their least upper bound:
The forward implication is the defining leastness property; the reverse uses that is an upper bound and transitivity. Consequently
Recomputing minimal elements therefore yields a finite basis for the exact intersection.
For subset inclusion, it suffices that every generator of the left-hand upper set belongs to the right-hand upper set. Necessity holds because each generator belongs to its own closure by reflexivity. Sufficiency holds because the right-hand set is upward closed. Finally,
Thus lub computes union, which is the least upper bound under subset inclusion. The empty basis represents the empty set; all four methods handle it without a special case. As in part (a), two distinct Scala objects may denote the same abstract ordered value. Retaining several equivalent representatives is harmless: they generate the same upward closure, and the strict-comparison test removes only genuinely nonminimal values.
Keep precisely those elements with no strictly smaller element in the input. Express strictness using the partial order itself, avoiding reliance on Scala reference equality.
def minimal[T <: Ordered[T]](xs: Set[T]): Set[T] =
  xs.filter { x =>
    !xs.exists(y => (y <= x) && !(x <= y))
  }
In a partial order, means and . Therefore
The empty set produces the empty set. An incomparable pair is retained in full; a minimal element need not be a least element. At most order comparisons are needed, and only the supplied F-bounded generic interface is required.
The interval containment order compares the lower endpoints in reverse and the upper endpoints in their ordinary order. The smallest containing integer interval uses the smaller lower endpoint and the larger upper endpoint.
class Interval(val l: Int, val u: Int) extends Ordered[Interval] {
  require(l <= u)
  def <=(that: Interval): Boolean = that.l <= l && u <= that.u
  def lub(that: Interval): Interval =
    new Interval(math.min(l, that.l), math.max(u, that.u))
}
Endpoint comparison immediately proves reflexivity, transitivity, and antisymmetry on interval values. If two separately allocated instances have the same endpoints, they denote the same abstract interval; object identity is not the mathematical equality used by this partial order.
The returned interval contains both operands. Every other containing interval has a lower endpoint at most both original lower endpoints and an upper endpoint at least both original upper endpoints, so it also contains the returned hull. Hence
This least upper bound need not equal the ordinary union: the hull of and is , including the gap.
The rightmost trait intercepts insertion first. In A, capitalization runs before counting, so the counter receives uppercase A and does not increment the lowercase count. In B, counting receives lowercase a, delegates to capitalization, and then increments the original lowercase argument's counter. Both concrete documents store uppercase A.
The exact output is
0
1
This illustrates an order-sensitive stackable trait modification. In the second composition the cached count describes intercepted insertion arguments, and no longer the actual lowercase characters in the stored document. For example, deleting its uppercase A would not decrement the lowercase counter. A counter required to track actual contents under arbitrary transforming mixins would need a stronger composition contract or would inspect the stored character after insertion; such a change would alter the intended behavior of this example.
Convert lowercase alphabet letters before passing them to the next implementation. Already uppercase letters and other characters pass through unchanged.
trait CapitalisePlugIn extends Doc {
  abstract override def insert(x: Char, pos: Int): Unit = {
    val y = if ('a' <= x && x <= 'z') ('A' + (x - 'a')).toChar else x
    super.insert(y, pos)
  }
}
The character conversion uses the paper's contiguous lowercase and uppercase Latin ranges. Thus
Deletion, reading, and size need no overrides.
Keep 26 cached counts, updating them after a successful delegated edit. Count exactly lowercase letters; the query alphabet does not make uppercase letters the same character.
trait CharTrackPlugIn extends Doc {
  private val counts = Array.fill[Int](26)(0)
  private def lower(x: Char): Boolean = 'a' <= x && x <= 'z'
  abstract override def insert(x: Char, pos: Int): Unit = {
    super.insert(x, pos)
    if (lower(x)) counts(x - 'a') += 1
  }
  abstract override def delete(pos: Int): Char = {
    val x = super.delete(pos)
    if (lower(x)) counts(x - 'a') -= 1
    x
  }
  def getCount(x: Char): Int = {
    assert(lower(x))
    counts(x - 'a')
  }
}
For an initially empty underlying document that stores inserted arguments unchanged, the representation invariant is
Insertion increments precisely the inserted lowercase character's count. Deletion uses the returned character, avoiding an extra indexed read and decrementing the character actually removed. Updating after super means a rejected edit leaves the counts unchanged. Both the added update work and getCount use time complexity and fixed storage.
The abstract override methods implement a stackable trait modification: they require a concrete document implementation further along the delegation chain. The invariant above depends on subsequent layers preserving inserted characters. Part (d) deliberately composes a transforming layer that can break this assumption; that is the transform-sensitive trait frequency counter phenomenon.
Store characters in a mutable dynamic array. Its length is maintained by the collection, giving constant-time size.
class DocImpl extends Doc {
  private val text = scala.collection.mutable.ArrayBuffer.empty[Char]
  def insert(x: Char, pos: Int): Unit = {
    require(0 <= pos && pos <= text.length)
    text.insert(pos, x)
  }
  def delete(pos: Int): Char = {
    require(0 <= pos && pos < text.length)
    text.remove(pos)
  }
  def read(pos: Int): Char = {
    require(0 <= pos && pos < text.length)
    text(pos)
  }
  def size: Int = text.length
}
The representation invariant requires a nonnull private dynamic array containing a finite sequence of characters, with its internal collection invariant maintained by the library. The abstraction function is
Insertion allows the end position and shifts the suffix right; deletion returns exactly the removed character and shifts the suffix left. read leaves the abstract state unchanged. size equals the abstract sequence length in time complexity. Indexed reads are also constant time; insertion and deletion may take linear time, which the question permits.
In Scala trait linearization, the rightmost mixed-in trait takes priority. Merge inherited linearizations while retaining a shared ancestor only at its final position; super advances to the next implementation in the resulting chain. Ignoring predefined bases, the lookup orders are
Life1, Exercise, Work, Life
Life2, Dance, Exercise, Program, Study, Work, Fun, Life
Life3, Program, Study, Work, Dance, Fun, Exercise, Life
For example, Dance alone has order Dance, Fun, Exercise, Life. In Life2, the additional Program branch already contains Fun before Life; keeping that shared ancestry in its later position moves Fun after Work, while Dance still takes priority. Reversing the mixins yields the third chain above.
Each describe first delegates and then prints, so the lines appear in the reverse of the method lookup order. The exact output, including the two blank lines, is
I live.
I work.
I exercise.

I live.
I have fun.
I work.
I study.
I program.
I exercise.
I dance.

I live.
I exercise.
I have fun.
I dance.
I work.
I study.
I program.
The super inside a trait therefore need not call that trait's isolated textual parent; the receiving object's full Scala trait linearization controls the next body.

Pinned article: Introduction to the OurBigBook Project

Welcome to the OurBigBook Project! Our goal is to create the perfect publishing platform for STEM subjects, and get university-level students to write the best free STEM tutorials ever.
Everyone is welcome to create an account and play with the site: ourbigbook.com/go/register. We belive that students themselves can write amazing tutorials, but teachers are welcome too. You can write about anything you want, it doesn't have to be STEM or even educational. Silly test content is very welcome and you won't be penalized in any way. Just keep it legal!
We have two killer features:
  1. topics: topics group articles by different users with the same title, e.g. here is the topic for the "Fundamental Theorem of Calculus" ourbigbook.com/go/topic/fundamental-theorem-of-calculus
    Articles of different users are sorted by upvote within each article page. This feature is a bit like:
    • a Wikipedia where each user can have their own version of each article
    • a Q&A website like Stack Overflow, where multiple people can give their views on a given topic, and the best ones are sorted by upvote. Except you don't need to wait for someone to ask first, and any topic goes, no matter how narrow or broad
    This feature makes it possible for readers to find better explanations of any topic created by other writers. And it allows writers to create an explanation in a place that readers might actually find it.
    Figure 1.
    Screenshot of the "Derivative" topic page
    . View it live at: ourbigbook.com/go/topic/derivative
  2. local editing: you can store all your personal knowledge base content locally in a plaintext markup format that can be edited locally and published either:
    This way you can be sure that even if OurBigBook.com were to go down one day (which we have no plans to do as it is quite cheap to host!), your content will still be perfectly readable as a static site.
    Figure 5. . You can also edit articles on the Web editor without installing anything locally.
    Video 3.
    Edit locally and publish demo
    . Source. This shows editing OurBigBook Markup and publishing it using the Visual Studio Code extension.
  3. https://raw.githubusercontent.com/ourbigbook/ourbigbook-media/master/feature/x/hilbert-space-arrow.png
  4. Infinitely deep tables of contents:
    Figure 6.
    Dynamic article tree with infinitely deep table of contents
    .
    Descendant pages can also show up as toplevel e.g.: ourbigbook.com/cirosantilli/chordate-subclade
All our software is open source and hosted at: github.com/ourbigbook/ourbigbook
Further documentation can be found at: docs.ourbigbook.com
Feel free to reach our to us for any help or suggestions: docs.ourbigbook.com/#contact