Thursday 31 December 2015

Sequence Control

  1. Sequence control
Sequence control : the control of the order of execution of the operations both primitive and user defined.
Implicit: determined by the order of the statements in the source program
or by the built-in execution model
Explicit: the programmer uses statements to change the order of execution
(e.g. uses If statement)
  1. Levels of sequence control
Expressions: computing expressions using precedence rules and parentheses.
Statements: sequential execution, conditional and iteration statements.
Declarative programming: an execution model that does not depend on the order
of the statements in the source program.
Subprograms: transfer control from one program to another.
  1. Sequencing with expressions
The issue: given a set of operations and an expression involving these operations,
what is the sequence of performing the operations?
How is the sequence defined, and how is it represented?
An operation is defined in terms of an operator and operands.
The number of operands determines the arity of the operator.
Basic sequence-control mechanism: functional compositionGiven an operation with its operands, the operands may be:
  • Constants
  • Data objects
  • Other operations
Example 1: 3 * (var1 + 5)
    operation - multiplication, operator: *, arity - 2
    operand 1: constant (3)
    operand 2: operation addition
    operand1: data object (var1)
    operand 2: constant (5)
Functional compositions imposes a tree structure on the expression,
where we have one main operation, decomposable into an operator and operands.
In a parenthesized expression the main operation is clearly indicated.
However we may have expressions without parentheses.
Example 2: 3* var1 +5
Question: is the example equivalent to the above one?
Example 3: 3 + var1 +5
Question: is this equivalent to (3 + var1) + 5, or to 3 + (var1 + 5) ?
In order to answer the questions we need to know:
  • Operator's precedence
  • Operator's associativity
Precedence concerns the order of applying operations, associativity deals with the order of operations of same precedence.
Precedence and associativity are defined when the language is defined - within the semantic rules for expressions.
3. 1. Arithmetic operations / expressionsIn arithmetic expressions the standard precedence and associativity of operations
are applied to obtain the tree structure of the expression.
Linear representation of the expression tree:
  • Prefix notation
  • Postfix notation
  • Infix notation
Prefix and postfix notations are parentheses-free.
There are algorithms to evaluate prefix and postfix expressions and algorithms to convert an infix expression into prefix/postfix notation, according to the operators' precedence and associativity.
3. 2. Other expressionsLanguages may have some specific operations, e.g. for processing arrays and vectors, built-in or user defined. Precedence and associativity still need to be defined - explicitly in the language definition or implicitly in the language implementation.
3. 3. Execution-time representation of expressions
  • Machine code sequence
  • Tree structures - software simulation
  • Prefix or postfix form - requires stack, executed by an interpreter.
3. 4. Evaluation of tree representationEager evaluation - evaluate all operands before applying operators.
Lazy evaluation - first evaluate all operands and then apply operations
Problems:
  • Side effects - some operations may change operands of other operations.
  • Error conditions - may depend on the evaluation strategy (eager or lazy evaluation)
  • Boolean expressions - results may differ depending on the evaluation strategy.
  1. Statement level sequence control
4. 1. Forms of statement-level control
  • Composition – Statements are executed in the order they appear on the page.
  • Alternation – Two sequences form alternatives so one sequence or the other 
  • sequence is executed but not both. (conditionals)
  • Iteration – A sequence of statements that are executed repeatedly.
  • Explicit Sequence Control
goto Xif Y goto X – transfer control to the statement labeled X if Y is true.
break4. 2. Structured programming design
  1. Hierarchical design of program structures
  1. Representation of hierarchical design directly in the program text 
  1. using "structured" control statements.
  1. The textual sequence corresponds to the execution sequence
  1. Use of single-purpose groups of statements
4. 3. "Structured" control statements
  1. Compound statements
Typical syntax:
begin
    statement1;
    statement2;
    ...
    end;
Execute each statement in sequence.
Sometimes (e.g., C) { ... } used instead of begin ... end
  1. Conditional statements
if expression then statement1 else statement2
if expression then statement1
If we need to make a choice among many alternatives
nested if statements
case statements
Example :
case Tag is
    when 0 => begin
    statement0
      end;
when 1 => begin
    statement1
      end;
when 2 => begin
    statement2
      end;
when others => begin
    statement3
       end;
end case
Implementation: jump and branch machine instructions, jump table implementation for case statements (see fig. 8.7)
  1. Iteration statements
Simple repetition (for loop) Specify a count of the number of times to execute a loop:
Examples:
perform statement K times;
for I=1 to 10 do statement;
for(I=0;  I<10;  I++) statement;
Repetition while condition holds
while expression do statement; - Evaluate expression and if true execute statement. then repeat process.
repeat statement until expression; - Execute statement and then evaluate expression. Quit if expression is true.
C++ for loop functionally is equivalent to repetition while condition holds
by T. Pratt and M. ZelkowitzProblems with structured sequence control:Multiple exit loops
Exceptional conditions
Do-while-do structure







Characteristics of Programming Languages

  • Readability: A good high-level language will allow programs to be written in some ways that resemble a quite-English description of the underlying algorithms. If care is taken, the coding may be done in a way that is essentially self-documenting.
  • Portability: High-level languages, being essentially machine independent, should be able to develop portable software.
  • Naturalness:- A good language should be natural for the application area, for which it has been designed. That is, it should provide appropriate operators, data structures,  control structures, and a natural syntax to facilitate the users to code their problem easily and efficiently.  
  • Generality: Most high-level languages allow the writing of a wide variety of programs, thus relieving the programmer of the need to become expert in many diverse languages.
  • Brevity: Language should have the ability to implement the algorithm with less amount of code. Programs expressed in high-level languages are often considerably shorter than their low-level equivalents.
  • Error checking: Being human, a programmer is likely to make many mistakes in the development of a computer program. Many high-level languages enforce a great deal of error checking both at compile-time and at run-time.
  • Cost: The ultimate cost of a programming language is a function of many of its characteristics.
  • Familiar notation: A language should have familiar notation, so it can be understood by most of the programmers.
  • Quick translation: It should admit quick translation.
  • Efficiency: Programs written in a good programming language are efficiently translated into machine code, are efficiently executed, and acquire as little space in the memory as possible.  
  • Modularity: It is desirable that programs can be developed in the language as a collection of separately compiled modules, with appropriate mechanisms for ensuring self-consistency between these modules.
  • Widely available: Language should be widely available and it should be possible to provide translators for all the major machines and for all the major operating systems. 

Dataflow Programming Language

   A programming paradigm in which computation is modelled as a directed graph (which may or may not contain cycles), the nodes of which are either data sources (producers of data), data sinks (consumers), or "processing elements" which compute some function; and the arcs of which represent dataflow between nodes.

   Dataflow programming languages share some features of functional languages, and were generally developed in order to bring some functional concepts to a language more suitable for numeric processing.

Representation

Dataflow programs start with an input, perhaps the command line parameters, and illustrate how that data is used and modified. The flow of data is explicit, often visually illustrated as a line or pipe.

In terms of encoding, a dataflow program might be implemented as a hash table, with uniquely identified inputs as the keys, used to look up pointers to the instructions. When any operation completes, the program scans down the list of operations until it finds the first operation where all inputs are currently valid, and runs it. When that operation finishes, it will typically output data, thereby making another operation become valid.

 

Introduction To Lambda Calculus

1. Introduction

   The Lambda Calculus was developed by Alonzo Church in the 1930s and published in 1941 as "The Calculi Of Lambda Conversion". It became important, along with Turing machines, in the development of computation theory, and is the theoretical basis of all functional programming languages, such as Lisp, Haskell and ML.

   Lambda calculus is a formal, abstract language where all functions are defined without giving a name. We can understand the foundations of functional programming by studying the properties of this formal language.

   It should be noted that the lambda notation is not frequently used in practical Lisp programming. The reason is simple: the use of a name is more convenient to refer to a function. Reference to a function without name is difficult for practical programming if you want the function to be called many times. However, as we have said, our interest lies in the role that lambda calculus plays as a foundation of functional programming. Once we understand an abstract model of computation, we can always develop a language based on it. 

   It does not have any complicated formulae or operations. All it ever does is taking a line of letters (or symbols), and performing a little cut and paste operation on it. As you will see, the Lambda Calculus can compute everything that can be computed, just with a very simple cut and paste.

2. Language

Syntax of lambda calculus
 
The language of lambda calculus is simply defined as:
[function]      := (lambda (x) [expression])
[application]   := ([expression] [expression])
[expression]    := [identifiers] | [application] | [function]
[identifiers]   := a | b | ...

    A line of symbols is called an expression. It might look like this: (λx.xy) (ab)
We only have the following symbols:

  • Single letters (like a, b, c, d...), which are called variables. An expression can be a single letter, or several letters in a row. More generally, we can write any two or more expressions together to get another expression.
  • Parentheses: ( ). Parentheses can be used to indicate that some part of an expression belongs together (just as the braces around this part of the sentence make it belong together). Where we don't have parentheses, we look at expressions simply from left to right.
  • The greek letter λ (pronounced, of course: Lambda), and the dot: . With λ and the dot, we can write functions. A function starts always with the λ and a variable, followed by a dot, and then comes an expression. The λ does not have any complicated meaning: it just says that a function starts here. The λ-variable-. part of a function is called its head, and the remainder (the expression) is called the body.     


Q: What is the value or meaning of a variable? 
A: None. Variables do not stand for anything. They are just empty names. Even the name is unimportant. The only thing that matters is: when two variables have the same name, they are the same. You can rename variables all you want, without changing the expression.
Q: What does a function calculate? 
A: Nothing, really. It is just a kind of expression, with a head and a body. It just stands there. The only thing we can do with it is to resolve it.
Q: Why "λ"? 
A: An accident, perhaps. Initially, Alonzo Church just drew a little roof to mark the head variable, like this: (ŷ xy) ab. In the typed manuscript, he put the roof in front of the head, so it became (⋀y.xy) ab. The typesetter turned it into (λy.xy) abwhich is visually close enough.

    Slightly more formally, we can say: All variables are lambda terms (a valid expression in the lambda calculus). If x and are lambda terms, then (x y) is a lambda term, and (λx.y) is a lambda term. From these three rules, we can construct all valid expressions. If we also agree to read all lambda expressions from left to right, we can omit a few of the parenthesis: (λy.xy) ab is the simplified version of (((λy.(x y)) a) b).

Cut & Paste

    Functions can be resolved if they are followed by another expression. The resolution works by taking the variable mentioned in the head, and replacing all of its occurrences within the body with the expression after the function. We cut the expression after the function, and paste it into the body, in every place indicated by the head. Having done that, we throw the head away, because it has served its purpose: telling us which variable to replace.
  
   The resolution of functions is the only thing we can ever do in the Lambda Calculus. Once we have gotten rid of all the lambdas, or if there are no more expressions after the remaining functions, we cannot replace anything any more. We can go home now. 

      Q: Can functions contain other functions?
   
      A: Absolutely. Functions are expressions, and expressions can contain other expressions, so functions can be parts of the bodies of other functions, or be part of the replacing expression. In fact, we have expressions like λx.λy.xzy so often that we like to abbreviate them as λxy.xzy. This means that we will try to replace the first variable in the head (x) with the first expression after the body (xzy), the second variable (y) with the next one after that, and so on.  The variables mentioned in the head (the one tagged for replacement) are called bound variables. Unmentioned variables are free variables. Because functions can be part of other functions, a variable may be both bound and free in the same expression.

Church Numerals

    So if we want to have numbers, we have to encode them as functions. Thankfully, Alonzo Church already came up with such an encoding, where the value of a numeral is equivalent to the number of times a function is applied to an argument. This can be written as λs.λz. sn z, where n is the natural number represented and sn means function s composed with itself n times (we’ll say “applied n times” for short).
Thus the first few natural numbers are encoded as follows:
  • 0 = λs.λz. z the function s is applied to the argument z zero times
  • 1 = λs.λz. s z the function s is applied once
  • 2 = λs.λz. s (s z) the function s is applied twice
(the names s and z are short for successor and zero)

    Lets also quickly look at how we can write simple functions with numbers.
succ = λn.λs.λz. s (n s z) (n + 1)
succ adds 1 to a number. Since a number n is defined as a function that applies the 1st argument to the 2nd argument n times, the result should be a function that applies the 1st argument one more time. When you substitute the variable n in succ with a value x, that’s what you get: a function that applies s one more time than x would:
(λn.λs.λz. s (n s z)) x → λs.λz. s (x s z)
    Similarly, we define a + b so that it returns a function applying s b times and then a times
add = λa.λb.λs.λz. a s (b s z) (a + b)
    To understand this more clearly, lets substitute values x and y for a and b:
(λa.λb.λs.λz. a s (b s z)) x y →
(λb.λs.λz. x s (b s z)) y →
λs.λz. x s (y s z)
    As we can see, after applying x and y, the result is still a function that looks similar in shape to our original definition of numbers. It applies s to z y times, then applies s to the result x more times. Multiplication can be defined in a way that looks even simpler than the addition above:
mul = λa.λb.λs. a b s (a * b)
Substituting x and y gives us:
(λa.λb.λs. a b s) x y →
(λb.λs. x b s) y →
λs. x y s
    It looks simpler, but is perhaps harder to grasp immediately: we used a trick and left out the z argument. Now the shape of the resulting function is different — it applies y to s x times, but what does it mean to apply a number y to just one argument? Remember that if y is a number, it must perform a computation of the shape λs.λz. sy z. If we apply this to the s in the multiplication x times, we get the following (renaming the outer s to s’ to distinguish from the inner s):
λs’. (λs.λz. sy z)x s’
   So in this case we see that the 2nd argument z is there in y, but we apply y only to s. This is similar to partial application in many languages. Lets see what happens if x = y = 1:
λs’. (λs.λz. s1 z)1 s’ =
λs’. (λs.λz. s z) s’ →
λs’.λz. s’ z

(this is equivalent to the definition of 1)
Lets do 2 * 2 as well, where the substitutions become more complex and we rename some variables to distinguish similarly-named ones:
λs’. (λs.λz. s2 z)2 s’ =
λs’. (λs.λz. s (s z))2 s’ =
λs’. (λs.λz. s (s z)) ((λs’’.λz’’. s’’ (s’’ z’’)) s’) →
λs’. (λs.λz. s (s z)) (λz’’. s’ (s’ z’’)) →
λs’. λz. (λz’’. s’ (s’ z’’)) ((λz’’. s’ (s’ z’’)) z) →
λs’. λz. (λz’’. s’ (s’ z’’)) (s’ (s’ z)) →
λs’. λz. s’ (s’ (s’ (s’ z))))

(this is equivalent to definition of 4)
To recap, we defined multiplication using partial application of lambdas, without mentioning the second argument z that we usually had in numbers, but the end result still has the correct shape. We could also have defined multiplication in a more verbose way that includes the z argument. Give it a try, or try to come up with an alternative definition of multiplication that uses succ or add.

Church Booleans

   Booleans can also be encoded as functions:
  • true = λt.λf.t 2-arg function returning the 1st arg
  • false = λt.λf.f 2-arg function returning the 2nd arg
    Basically, the booleans represent if-then-else expressions. They both take two arguments, true returns the first (then) and false returns the second (else). We’ll take a look at some example programs with booleans in the next posts.



 

 
 

Thursday 24 December 2015

OpenGL Primitives

   Primitives are basic shapes that you can easily draw. It can be a triangle, a square, or even a single point.

   First, let me mention that OpenGL is a low level API, this means that it has no support for drawing complex geometrical objects. It is the programmer’s job to combine the geometrical primitives from OpenGL in complex shapes and bodies. The basic geometrical primitives that the core OpenGL profile provide to us are points, lines and triangles.

   Each primitive has at least one vertex. What is a vertex exactly? Is it one of the points in a polygon or is it a stand-alone point in space? Well it can be both, depending on how you think about it. With points, vertex is just that - the point.
  • A line has only 2 vertices - its starting point and its ending point.
  • With polygons, there should be more than 2 vertices since polygons are surfaces defined by more or equal to 3 vertices residing on the same plane. 
  • A triangle is, for instance, a polygon with 3 vertices.  

   Note that a 3D cube cannot be considered a primitive. Generally, primitives restrict themselves to triangles. A four-sided polygon can generate a quad but that quad will still be made out of 2 polygons. Points and lines can also be considered primitives.

Immediate Mode

   The easiest way to do drawing in OpenGL is using the Immediate Mode. For this, you use the glBegin() function which takes as one parameter the “mode” or type of object you want to draw.

   Here is a list of the possible modes and what they mean:

GL_POINTS Draws points on screen. Every vertex specified is a point.
GL_LINES Draws lines on screen. Every two vertices specified compose a line.
GL_LINE_STRIP Draws connected lines on screen. Every vertex specified after first two are connected.
GL_LINE_LOOP Draws connected lines on screen. The last vertex specified is connected to first vertex.
GL_TRIANGLES Draws triangles on screen. Every three vertices specified compose a triangle.
GL_TRIANGLE_STRIP Draws connected triangles on screen. Every vertex specified after first three vertices creates a triangle.
GL_TRIANGLE_FAN Draws connected triangles like GL_TRIANGLE_STRIP, except draws triangles in fan shape.
GL_QUADS Draws quadrilaterals (4 – sided shapes) on screen. Every four vertices specified compose a quadrilateral.
GL_QUAD_STRIP Draws connected quadrilaterals on screen. Every two vertices specified after first four compose a connected quadrilateral.
GL_POLYGON Draws a polygon on screen. Polygon can be composed of as many sides as you want.

   All the shapes that you can think of either are, or can be broken down into, geometric primitives. For example a pyramid can be broken down into a few triangles. All the geometry that you render in OpenGL is constructed of primitives and there are quite a few available to you. Below is a list of the OpenGL primitive types available and an example of how they can look with an example set of vertices:



    The main function (and probably the most used OpenGL function) is function named glVertex. This function defines a point (or a vertex) in your 3D world and it can vary from receiving 2 up to 4 coordinates.
    Let's take a look at these variations:

   glVertex2f(100.0f, 150.0f); defines a point at x = 100, y = 150, z = 0; this function takes only 2 parameters, z is always 0. glVertex2f can be used in special cases and won't be used a lot unless you're working with pseudo-2D sprites or triangles and points that always have to be constrained by the depth coordinate.

   glVertex3f(100.0f, 150.0f, -25.0f); defines a point at x = 100, y = 150, z = -25.0f; this function takes 3 parameters, defining a fully 3D point in your world. This function will be used a lot to define any kind of shapes you will possibly want.

   glVertex4f(100.0f, 150.0f, -25.0f, 1.0f); this is the same as glVertex3f, the only difference is in the last coordinate that specifies a scaling factor. The scaling factor is set to 1.0f by default. This won't make a lot of use and I'm not going to explain this function in details. It can be used to make your 3D points look thicker than one pixel. I don't want to sound pathetic but why would you want to use that functionality?

   Anyway, glVertex alone won't draw anything on the screen, it merely defines a vertex, usually of a more complex object. To really start displaying something on the screen you will have to use two additional functions.
 
   These functions are: glBegin(int mode) and glEnd( void ).

    glBegin and glEnd delimit the vertices of a primitive or a group of like primitives. What this means is that everytime you want to draw a primitive on the screen you will first have to call glBegin, specifying what kind of primitive it is that you want to draw in the mode parameter of glBegin, and then list all vertices one by one (by sequentially calling glVertex) and finally call glEnd to let OpenGL know that you're done drawing a primitive.

Drawing Points

  Lets do an example with the simplest mode, the GL_POINTS. When drawing points using OpenGL, the default size of the points is 1 pixel wide and high. This would be very hard to see when you run the program. To edit the size of the point you want to draw, you use the glPointSize() function which takes as one parameter the size of the point you want.

  Now in the Render() function, before you write the glBegin() code, we will set the point size to be 10 pixels in size:

    glPointSize(10.0f); 
 
   After that, any drawing of points will be drawn 10 pixels wide and high. Now write the glBegin() function with the GL_POINTS mode parameter. Then after that specify the vertices you want to use using the glVertex3f() function. For this example, we want the upper – right corner (1.0,1.0,0.0) and the lower – left corner (-1.0,-1.0,0.0) of the screen to have a point. After drawing those two points, make sure to end the drawing with the glEnd() function:

    glBegin(GL_POINTS); //starts drawing of points
      glVertex3f(1.0f,1.0f,0.0f);//upper-right corner
      glVertex3f(-1.0f,-1.0f,0.0f);//lower-left corner
    glEnd();//end drawing of points 
 

Drawing Triangles

   Triangles are composed of three vertices. For this example we are going to use the regular GL_TRIANGLES mode to draw two triangles side by side.

   First we want a triangle to the left. So we need three vertices on the left side representing one triangle and three vertices on the right side of the window representing the second triangle. Note that you don’t need two glBegin() functions to draw two triangles. Since the mode GL_TRIANGLES is plural, it can handle more than one triangle in between one glBegin() and glEnd() function call:
 



   Note that I wrote on the bottom – right corner of the diagram the coordinates of the vertices. Here is the code to draw these two triangles:

    glBegin(GL_TRIANGLES);//start drawing triangles
      glVertex3f(-1.0f,-0.25f,0.0f);//triangle one first vertex
      glVertex3f(-0.5f,-0.25f,0.0f);//triangle one second vertex
      glVertex3f(-0.75f,0.25f,0.0f);//triangle one third vertex
      //drawing a new triangle
      glVertex3f(0.5f,-0.25f,0.0f);//triangle two first vertex
      glVertex3f(1.0f,-0.25f,0.0f);//triangle two second vertex
      glVertex3f(0.75f,0.25f,0.0f);//triangle two third vertex
    glEnd();//end drawing of triangles

Wednesday 23 December 2015

JavaBean

   JavaBean is a Java class that is mainly responsible for holding on to some data without a large degree of functionality built into the class.

   Typically, a JavaBean is a Java class that:
  1. Implements the Serializable interface
  2. Exposes its properties via getter/setter methods
  3. Has a no-argument constructor
  • Implementing the Serializable interface is required if you have a need to save the object (with its data) somewhere (file system, database, refrigerator, etc).
  •  It's standard practice is to allow the manipulation of the properties within the class via getter/setter methods. 
  • Sometimes we may add further methods to a JavaBean class besides the standard getter/setter methods if the nature of particular functionality makes sense to be encapsulated within the bean class, but we make an effort to make such changes the exception to the rule, since this can dirty up your bean classes, which can spread out functionality within your application which can make code maintainability more difficult.

Simple example of java bean class

  1. //Employee.java  
  2.   
  3. package mypack;  
  4. public class Employee implements java.io.Serializable
  5. {  
  6.    private int id;  
  7.    private String name;  
  8.   
  9.    public Employee()
  10.   {
  11.   }  
  12.   
  13.    public void setId(int id)
  14.   {
  15.      this.id=id;
  16.   }  
  17.   
  18.   public int getId()
  19.  {
  20.     return id;
  21.  }  
  22.   
  23.   public void setName(String name)
  24.  {
  25.     this.name=name;
  26.  }  
  27.   
  28.   public String getName()
  29.   {
  30.     return name;
  31.   }  

How to access the java bean class?

To access the java bean class, we should use getter and setter methods.
  1. <jsp:useBean id="id" class="bean's class" scope="bean's scope">
       <jsp:setProperty name="bean's id" property="property name"  
                        value="value"/>
       <jsp:getProperty name="bean's id" property="property name"/>
       ...........
    </jsp:useBean>

JavaBeans Properties:

   A JavaBean property is a named attribute that can be accessed by the user of the object. The attribute can be of any Java data type, including classes that you define.
  A JavaBean property may be read, write, read only, or write only. JavaBean properties are accessed through two methods in the JavaBean's implementation class:
MethodDescription
getPropertyName()For example, if property name is firstName, your method name would be getFirstName() to read that property. This method is called accessor.
setPropertyName()For example, if property name is firstName, your method name would be setFirstName() to write that property. This method is called mutator.
    A read-only attribute will have only a getPropertyName() method, and a write-only attribute will have only a setPropertyName() method.

Tuesday 22 December 2015

Thin & Thick Client

Thin Client 
  
    A Thin client is a lightweight computer that is purpose built for remoting into a server (typically desktop virtualization resources). It depends heavily on another computer (its server) to fulfill its computational roles.
   In Thin clients many clients share their computations with a server or server farm. The server-side infrastructure makes use of cloud computing software such as application virtualization, hosted shared desktop (HSD) or desktop virtualization (VDI). This combination forms what is known today as a cloud based system where desktop resources are centralized into one or more data centers. The benefits of centralization are hardware resource optimization, reduced software maintenance, and improved security.
  • Example of hardware resource optimization: Cabling, bussing and I/O can be minimized while idle memory and processing power can be applied to users sessions that most need it.
  • Example of reduced software maintenance: Software patching, security updates, application/OS updates, and OS migrations can be applied, tested and activated for all users in one instance to accelerate roll-out and improve administrative efficiency.
  • Example of improved security: Software assets are centralized and easily fire-walled, monitored and protected. Sensitive data is uncompromised in cases of desktop loss or theft.
   
   Thin client hardware generally consists of a computer terminal which provides I/O for a keyboard, mouse, monitor, jacks for sound peripherals, and open ports for USB devices, e.g. printer, flash drive, web cam, card reader, smartphone, etc. Some thin clients include legacy serial and/or parallel ports to support older devices such as receipt printers, scales, time clocks, etc. 

   Thin client software typically consists of a GUI (graphical user interface), cloud access agents (e.g. RDP, ICA, PCoIP), a local web browser, terminal emulations (in some cases), and a basic set of local utilities.

Characterstics of thin clients 

Architecture: 

    In using cloud based architecture, the server takes on the processing load of several client sessions, acting as a host for each endpoint device. The client software is narrowly purposed and lightweight; therefore, only the host server or server farm needs to be secured, rather than securing software installed on every endpoint device (although thin clients may still require basic security and strong authentication to prevent unauthorized access). One of the trade-offs of using cloud architecture and thin clients is that the host becomes a single point of failure. If the hosting server crashes, for example, all users lose their session unless a fail-over process is predefined. 

  The simplicity of thin client hardware and software results in a very low TCO (total cost of ownership), but some of these initial savings can be offset by the need for a more robust cloud infrastructure required on the server side. NOTE: infrastructure costs can be spread out by choosing a subscription based cloud model known as Desktop as a Service or DaaS. DaaS allows IT organizations to outsource the cloud infrastructure to a third party. 

Simplicity:

   Thin client computing is known to simplify the desktop endpoints by reducing the client-side software footprint. With a lightweight, read-only OS, client-side setup and administration is greatly reduced. Cloud access is the primary role of a thin client which eliminates the need for a large suite of local user applications, data storage and utilities. This architecture shifts the most of software execution burden from the endpoint to the data center. 

Hardware

   While the server must be robust enough to handle several client sessions at once, thin client hardware requirements are minimal to that of a traditional PC desktop. Most thin clients have low energy processors, flash storage, memory, and no moving parts. This reduces the cost and power consumption, making them affordable to own and easy to replace or deploy. Since thin clients consist of fewer hardware components than a traditional desktop PC, they can operate in more hostile environments. And because they typically don't store critical data locally, risk of theft is minimized because there is little or no user data to be compromised.

Limitations

   To achieve such simplicity, thin clients sometimes lag behind desktop PCs in terms of extensibility. For example, if a local software utility or set of device drivers are needed in order to support a locally attached peripheral device (e.g. printer, scanner, biometric security device), the thin client operating system may lack the resources needed to fully integrate the required dependencies (although dependencies can sometimes be added if they can be identified). Modern thin clients address this limitation via port mapping or USB redirection software. However, these methods cannot address all use case scenarios.

 Thick Client

   A fat client (also called heavy, rich or thick client) is a computer (client) in client–server architecture or networks that typically provides rich functionality independent of the central server. Originally known as just a "client" or "thick client".

     A fat client still requires at least periodic connection to a network or central server, but is often characterised by the ability to perform many functions without that connection. In contrast, a thin client generally does as little processing as possible and relies on accessing the server each time input data needs to be processed or validated.

Advantages 

  • Lower server requirements. A thick client server does not require as high a level of performance as a thin client server (since the thick clients themselves do much of the application processing). This results in drastically cheaper servers. 
  • Working offline. Thick clients have advantages in that a constant connection to the central server is often not required.
  • Better multimedia performance. Thick clients have advantages in multimedia-rich applications that would be bandwidth intensive if fully served. For example, thick clients are well suited for video gaming.
  • More flexibility. On some operating systems software products are designed for personal computers that have their own local resources. Running this software in a thin client environment can be difficult.
  • Using existing infrastructure. As many people now have very fast local PCs, they already have the infrastructure to run thick clients at no extra cost.
  • Higher server capacity. The more work that is carried out by the client, the less the server needs to do, increasing the number of users each server can support.


Difference between Thin Client & Thick Client