Skip to main content

12 posts tagged with "CS"

View All Tags

CS2100 Computer Organisation Notes

· 8 min read

P.S. The following information is based on various resources from the course CS2100 NUS, certain parts are adapted to include my comments and explanations for my better understanding & review. I will try to include source in relevant section as much as possible to honor the authors' work (Mainly Prof Aaron & Prof Colin & TA Alvin).

P.P.S Half way through the course, I decided to change the way I take notes and therefore the information contained here only cover the first few chapters of the module.


C

  • Read Array
// while-loop
int readArray(int arr[], int limit) {
int index, input;
printf("Enter up to %d integers, terminating with a negative integer.\n", limit);
index = 0; // Must remember to initialize to zero
scanf("%d", &input);
while (input >= 0) {
arr[index] = input;
index++;
scanf("%d", &input);
}
return index; // Which is the actual array size
}
// for-loop
int readArray2(int arr[], int limit) {
int index, input;
printf("Enter up to %d integers, terminating with a negative integer.\n", limit);
for (index = 0; index < limit; index ++) {
if (arr[index] < 0) {
break;
}
scanf("%d", &arr[index]);
}
return index - 1; // Which is the actual array size
}
  • Reverse Array
// iterative while-loop
void reverseArray(int arr[], int size) {
int left = 0, right = size - 1, temp;
while (left < right) {
temp = arr[left];
arr[left] = arr[right];
arr[right] = temp;
left++;
right--;
}
}
// iteractive for-loop
void reverseArray2(int arr[], int size) {
int start, temp, end = size - 1;
for (start = 0; start < size / 2; start++) {
temp = arr[start];
arr[start] = arr[end];
arr[end] = temp;
end--;
}
}
// recursive
void reverseArray3(int arr[], int size) {
reverseArrayRec(arr, 0, size - 1);
}

void reverseArrayRec(int arr[], int left, int right) {
int temp;
if (left < right) {
temp = arr[left];
arr[left] = arr[right];
arr[right] = temp;
reverseArrayRec(arr, left + 1, right - 1);
}
}
// recursive C-style : from TA Alvin CS2100
void reverseArray4(int arr[], int size) {
if (size <= 1) {
return;
} else {
temp = arr[0];
arr[0] = arr[size - 1];
arr[size - 1] = temp;
// Add 1 to array pointer => shift pointer by 4 bytes
// => move to next item in array, that is the new base
// => treat it as removing the front and back items,
// hence size - 2
reverseArray4(arr + 1, size - 2)
}
}

Sign extension

When we want to represent an n-bit signed integer as an m bit signed integer, where m > n. We do this by copying the sign-bit of the n-bit signed m - n times to the left of the n-bit number to create an m-bit number.

To show that sign extension is value-preserving, we can simply check two cases:

  • If the signed bit is 0, adding 0s to the left is similar to adding 0 to the right of a decimal point. It does not change the value.
  • If the signed bit is 1, adding 1s to the left seems to change the value, but not really. This is because the newly added 1s to the right of the newly added signed bit increases the value while the signed bit decreases the value of the number. This effectively cancels out the changes in value. E.g. Focusing on the value that signed bit and the extended signed bit portion, original: 1001 => -2**3 = -8, extended: 111001 => -2**5 + 2**4 + 2**3 = -8

Addition of two 1's complement number with decimal

  • Sign extend or pad zero to the end so that they are of equal length.
  • Invert all the bits for negative number to use addition.
  • If there is a carry out, add 1 to the result.
  • Check for overflow if the result is opposite sign of A and B.

Converting decimal numbers to fixed-point binary

When doing the rounding

  • 0.0000 is rounded to 0.000.
  • 0.0001 is rounded to 0.001.

Represent decimal value in IEEE 754 format

  • When converting the 32 bits to Hexadecimal, make sure that the signed bit is also included.

Bitwise operations

a = 001010
b = 101011
a | b = 101011
a & b = 001010
a ^ b = 100001
~a = 110101
a << 2 = 101000 # equivalent to a *= 4
a >> 2 = 001010 # equivalent to floor(b /= 4)

Special thing about XOR

x ^ x = 0
x ^ 0 = x

a = 00110
a = 00110
a ^ a = 00000

b = 00000
c = 00110
c ^ b = 00110

Swap without temporary variable, with bitwise operator

void swap(int *a, int *b) {
*a = *a ^ *b
*b = *a ^ *b # equivalent to *a ^ *b ^ *b = *a
*a = *a ^ *b # equivalent to *a ^ *b ^ *a = *b
}

MIPS Masking

Summary

  • andi/ori/xori use zero-extension to fill in upper 32 bits. The operation acts on all 32 bits.
andi x, 1 = x
andi x, 0 = 0
ori x, 0 = x
ori x, 1 = 1
nor x, 0 = ~x
xor x, 1 = ~x
xor x, 0 = x

Set bits 2, 8, 16 to 1 in b, a -> $s0, b -> $s1, c -> $s2

# 16th bit is outside the range of immediate, which is only 0 - 15th bit
# use intermediate registers for results
lui $t0, 0b1 # this sets the 16th bit
ori $t0, 0b0000000100000100 # this sets 0 - 15th bit
or $s1, $s1, $t0

Copy over bits 1, 3 of b into a

lui $t1, 0b1111111111111111
ori $t1, t1, 0b1111111111110101
and $s0, $s0, $t1 # clear the two bits from a
andi $t0, $s1, 0b0000000000001010 # get the two bits from b
or $s0, $s0, $t0 # copy the bits over

Make bits 2, 4 of c inverse of bits 1, 3 of b

xori $t0, $s1, 0b0000000000001010 # this invert and copy
andi $t0, $t0, 0b0000000000001010 # clear everything else
sll $t0, $t0, 1 # shift left to 2, 4
lui $t1, $t1, 0b1111111111111111 # clear out 2, 4 of c, 3 steps, lui, ori, and
ori $t1, $t1, 0b1111111111101011
and $s2, $s2, $t1
or $s2, $s2, $t0 # copy over to c

MIPS arithmetic

a -> $s0 b -> $s1 c -> $s2 d -> $s3

d = 6a + 3(b - 2c) d = 6a + 3b - 6c d = 3(2a + b - 2c) d = 3(2(a - c) + b)

sub $t0, $s0, $s2   # a - c
sll $t0, $t0, 1 # 2(a - c)
add $t0, $t0, $s1 # 2(a - c) + b
sll $s3, $t0, 2 # 4(2(a - c) + b)
sub $s3, $s3, $t0 # 3(2(a - c) + b)

MIPS tracing

add $t0, $s0, $ zero    # make a copy
lui $t1, 0x8000 # set t1 to 1 at MSB and 0 else where
lp: beq $t0, $zero, e # if t0 == zero, exit
andi $t2, $t0, 1 # t2 left with LSB
beq $t2, $zero, s # if t2 LSB == 0, s
xor $s0, $s0, $t1 # invert MSB
s: srl $t0, $t0, 1 # discard LSB
j lp # loop back
e: # exit

What happens when integer overflow in C

  • int is 4 bytes (in sunfire), which is 4 x 8 = 32 bits
  • the range is from -2,147,483,648 (-2^31) through +2,147,483,647(2^31 - 1)
  • This range comes from 32 bits 2s complement representation,
  • from 10000000000000000000000000000000 (smallest negative number)
  • to 01111111111111111111111111111111 (largest positive number)
  • when adding 1 to the largest positive number, it becomes the smallest negative number
  • adding another 1 will make it 10000000000000000000000000000001,
  • which is -2147483647, (literally add 1 to the decimal number)

For an n-bit sign-and-magnitude representation, what is the range of values that can be represented?

  • -(2^(n-1) - 1)
  • 2^(n-1) - 1

1s Complement

  • -x = 2^n - 1 - x (in decimal)
  • -x = 2^n - 1 - x + 1 (in decimal)
  • minus 1 because you want the largest possible values in that base

2s Complement

  • -2^(n - 1)
  • 2^(n-1) - 1
  • Adding A + B
  • Ignore the carry out of the MSB
  • Check for overflow. Overflow occurs if the carry in and the carry out of the MSB are different, or if result is opposite sign of A and B

CS2030/S Programming Methodology Overview

· 2 min read

P.S. The following information on CS2030/S Programming Methodology II is based on past experience and subject to changes. The purpose is to provide a rough idea of what is to come for prospective students so that one can prepare early if possible.

In the recent run of the module, "A module about abstractions" is Prof Henry's description for CS2030. Having the opportunity to be a lab tutor for this module, I think it is a very fair way to describe it.

ComponentsRemarks
Weekly labs (5%)- Need not complete the solution during lab
- Useful for checking concepts taught in lectures
- May need to refer to Java API for unfamiliar syntax
- It is perfectly normal to not be able to complete the labs or find them difficult during the lab sessions. This is partly because of unfamiliarity with Java APIs that are involved (e.g., use of Generics, Optionals, Streams)
Individual project (10%)- Two-part projects that require you to write small to medium scale programs
- Released during read week 1 & 2 (Or released incrementally throughout the semester)
- Difficulty lies in managing code complexity and adhering to design principles
- Strongly advised to start early and anticipate some serious work required to complete them on time
Practical assessment #1 in week 7 (15%)- 90 minutes levelized coding exercise
- Similar to lab exercises but tests all topics
Practical assessment #2 in week 12 (20%)- Similar to Practical 1, except in terms of difficulty
In-lecture quiz (5%)- Meant to test your understanding
Class participation and peer learning activities (5%)- Participate in posting/answering questions on Github issues
- Add notes on Github Wiki
Final exam (40%)- Structured questions that might involve writing of small Java programs to implement OOP design or other topics covered
- Format similar to PYPs but not really predictable as this depends on the setter

Updated as of: 28/4/2021

Painting Cats

· 7 min read

Screenshot

Suppose you have a row of N cats, numbered from 0 to N - 1. Initially, all the cats are painted black. You are asked to perform Q operations of the following two types, in random order. 1.Painting Cats: Given a range a to b(inclusive), cats within the range are painted with a new color. Example input:

Type of operation | Start index | End index | Color

1 0 10 blue

No output (void)

2.Query Cat: Given a particular number a, return the color of the cat numbered a. Example input:

Type of operation | number

2 10

Example output:

blue

It is guaranteed that 1 ≤ N ≤ 1,000,000,000 and 1≤Q ≤ 300,000. Create a time and space efficient solution.


I could not answer this question without a hint given by my classmate. Not a difficult question to understand, so feel free to think of a solution if you can, before reading on.

Question Analysis

The difficulty lies in the time and space complexity requirements. Let's ignore them for a while and come up with a naive solution.


Naive approach

One possible way to solve the problem is to simply do as told. Initialize an N sized array and assign every slot to be of the color blue. When handling paint-cat operation, simply loop over the array from the starting index to the end index, updating the existing color to the new color. During query-cat operation, simply index to the slot in the array and output the color in that slot.

There are a few issues with the above naive approach. One being that N could be too large to handle. This means our array will take out substantial space to solve this problem. Besides, the time complexity of the paint-cat operation is O(N) because we touch every cat within range per operation. The upside to this solution is that the query-cat operation is O(1) thanks to the array indexing. In summary:

Space:

  • O(N) array setup

Time:

  • O(N) per paint-cat, worst case O(QN), Q operations of paint-cat
  • O(1) per query-cat, worst case O(Q), Q operations of query-cat

Getting better

Looking at the limiting factors, realize that we cannot represent every single cat, or else we will have trouble making it space-efficient. As for the two operations, we need sublinear time complexity for both. There are a few choices, in terms of data structure, that can achieve sublinear complexity: ADTs:

Map (implemented by Hashtable):

O(1) insert, O(1) update, O(1) query

Priority Queue (implemented by Binary Heap)

O(logn) insert, O(n) update, O(logn) query

Ordered Map (implemented by Balanced Binary Search Tree)

O(logn) insert, O(logn) update, O(logn) query

Binary Heap is out since it requires O(n) update in its standard implementation, although it could achieve O(logn) update with an additional Hashtable to keep track of positions. Both Hashtable and bBST are possible. If we need to maintain some sort of order, we have to go with bBST.

Range Update, Point Query

The question, in effect, can be summarized by the above subheading. The key then is to represent the range and find out how we can query points from the new representation.

I thought about it for a long time. Certainly, we are employing a strategy just like using cones to mark out boundaries in a football field. A range of cats can be represented by the starting index and the ending index. To query a cat, we will look towards its left or right, till we find a demarcating cat. The approach is in the right direction. What puzzled me was how to update cats so that the boundaries are set correctly and point-query can be done properly.

Suppose we update the color of the starting index and the ending index cat. A few runs of operations are described as follows:

(10 cats)

starts with default color: Cat 0 => red, Cat 9 => red

Type of operation | Start index | End index | Color

1 2 5 blue

Cat 0 => red, Cat 2 => blue, Cat 5 => blue, Cat 9 => red

Now, if I employ the strategy where querying the cat will look for the nearest cat on its left that is colored, I can get away with the following queries

2 1 // Cat 0's color is red, hence Cat 1 is red

2 4 // Cat 2's color is blue, hence Cat 4 is blue

However, querying cats 6,7,8 will result in wrong output

2 7 // Cat 5's color is blue, hence Cat 7 is blue (WRONG)

There is also another issue with updating ranges that are overlapping:

// continuing the example above 1 1 3 green

Cat 0 => red, Cat 1 => green, Cat 2 => blue, Cat 3 => green, Cat 5 => blue, Cat 9 => red

Now that is a mess.

Solution

In the end, I needed two intuitions to fix the above issues.

  1. When querying, always look at the nearest colored cat on its left.
  2. When updating, ensure that the cats are painted in such a way that statement one will always return the correct output. In particular, updating a range also breaks down any previous ranges into separate ranges.

Breaking down the steps:

  1. Initialize an ordered map implemented by AVL tree (or any other balanced binary tree), in Java, use TreeMap.
  2. Insert index 0 and color red to mean all cats started with red.
  3. Per query, retrieve the successor of the given index in O(logn) and output the color.
  4. Per update, query(end index + 1) and insert end+1 index with the returned color. Clear the items in the tree that is within the updating range. Insert the starting index with the new color.

Instead of updating both the start index and the end index, we update the start index(new color) and end+1 index(marking the start of an existing color range). Also, we remove colored cats if they are within the new updated range. This way, Space:

  • O(Q) worst case, all operations are paint-cat, bBST

Time (n being the maximum number of cats in the bBST):

  • O(logn) per paint-cat, worst case O(Qlogn), Q operations of paint-cat
  • O(logn) per query-cat, worst case O(Qlogn), Q operations of query-cat

Sample Java solution as follows

import java.util.*;

public class PaintCats {
public static void main(String[] args) {
// custom fast io
Kattio k = new Kattio(System.in,System.out);

// read input
int numCat = k.getInt();
int numQ = k.getInt();

// init DS
TreeMap<Integer,String> cats = new TreeMap<>();

// predefined all cats to be Red
cats.put(0,'Red');

// read input
for (int i=0;i<numQ;i++){
int queryType = k.getInt();
if (queryType == 1){ // change color operations
int start = k.getInt();
int end = k.getInt();
String color = k.getWord();

if (end != numCat){ // if it is the last cat, no need to add in lower limit
// get color by looking at the lowerbound,since all the correct lowerbound + color will be
// in the treeMap
char endColor = cats.floorEntry(end+1).getValue();
cats.put(end+1,endColor);
}

// remove to save space + speed up look up, safe since start - end is updated here
cats.subMap(start,end).clear();

// put in new start
cats.put(start,color);

} else { // query color operations
int catNo = k.getInt();
// look for the color by checking the lowerbound since all lowerbound are correctly updated
k.println(cats.floorEntry(catNo).getValue());
}
}
k.close();
}
}

Visualization

Quick self-reminder: You cannot target id of only digits such as #1, #2 in CSS.

In CSS, identifiers (including element names, classes, and IDs in selectors) can contain only the characters [a-zA-Z0-9] and ISO 10646 characters U+00A0 and higher, plus the hyphen (-) and the underscore (_); they cannot start with a digit, two hyphens, or a hyphen followed by a digit.

P.S. Behind the scene, the visualization does not work exactly as described in the efficient solution above. It is quite a challenge to visualize Balanced Binary Search Tree 😂

Further P.S. Having a hard time making this mobile friendly, view it on wider display, Chrome or FireFox, if you can.

See it on codepen