Data Structure and Algorithm CSC
Data Structure and Algorithm CSC
A data structure is a way to store and organize data in order to facilitate access and modifications. Note that when
talking of data structures, databases are excluded even though they form one way of storing data.
2. DATA STRUCTURES
2.1.Primitive Types
This is the simplest data structure. They hold single entries such as one number, one date or a single character or
string. The table below shows the basic primitive types.
Data Data
Type Stores/Description
Format Types
The Byte type holds an integer in the range 0 to 255. Bytes are
Byte frequently used to access binary files, image and sound files, and
so on.
Integers Short Integer values in the range 32,768 to 32,767.
2.2.Linear Data Structure: This is data structure (container) that holds a sequence of elements arranged
linearly. Examples of linear data structures are:
The array we have just described is called one dimensional array or a vector. We also have n-dimensional
array (a matrix). A two dimensional array is structured as shown in Figure 2.2 below
0 1 2 3 4
1
23
2 Element in position (2,3)
2.2.2. List: A type of a data structure where one item points to it successor. A good way to think of a linked list
is to imagine a chain, where one element is written on each link. Once we get hold of one link of the chain,
we can retrieve all elements. The size of a list can be fixed (static list) or of variable size (dynamic list). A
static list can be implemented using an array while a dynamic list can be implemented using a linked-list
structure.
Linked-List
It is similar to an array except that it allows efficient insertion and removal of elements in any position in the list. This
aspect makes it advantageous over arrays. A linked list is also dynamic, that is, resizable. Each item has two fields: a
value and a pointer (link) to the next item. A single linked list is structured as shown in figure 2.2
2 4 7 5 null
2.3.Abstract Data Type: This is a data type in which the data and the functions that operate on the data are
defined but their implementation is not defined. It can have several different implementations thus can have
different efficiency. Examples of abstract data type:
2.4. Stack: A stack is a data structure with two basic methods- push and pop. It is has the Last In First Out (LIFO)
structure. A common picture is that of a pile of plates. The first plate begins the pile. The next is placed on top of
the first and the next on top of that, and so on. A plate may be removed from the pile at any time, but only from
the top. The order of pushing plates onto the pile or popping them from the top is arbitrary. There will always be
a certain number of plates on the pile. Pushing a plate onto the pile increases the number by one; popping a plate
decreases it by one. But naturally you cannot pop a plate off an empty pile doing so will lead to an error called
the under flow error. Nor can you push a plate onto a full one--at least if there is a maximum number of plates
the pile can hold doing so will lead to an over flow error. So these two conditions need to be monitored. A stack
can be implemented using array or linked-list.
2.5. Queue: A queue is similar to a stack, except that you join the queue at one end and leave it at the other. In it uses
the First In First Out (FIFO) structure. It could be any data items awaiting processing, print requests, instructions
awaiting execution, etc. It can be implemented using array. A queue is like the line of customers waiting to be
served by a bank teller. As customers arrive, the joint the end of the queue while the teller serves the customer at
the head of the queue.
2.6. Deque: This is a special type of a queue where data can be added and removed from both ends.
2.7.Tree: A tree is a non-empty collection of vertices (nodes or vertices) and edges that satisfies certain
requirements. A vertex can have a name, as well as carry other information. An edge connects two vertices. A
tree data structure is a powerful tool for organizing data objects based on keys. It is equally useful for organizing
multiple data objects in terms of hierarchical relationships (think of a ``family tree'', where the children are
grouped under their parents in the tree).
A path is a list of distinct vertices with successive vertices linked by edges. In a tree, there is exactly one path
between any pair of nodes
Types of Trees
- A rooted tree is one where one node is designated as the root of the tree.
- Every node in a rooted tree is the root of a sub tree consisting of the node and the nodes below it. There is
exactly one path between the root and each of the other nodes in a tree.
- Each node except the root node has exactly one node above it called its parent and the node below it is called
its children.
- Nodes with one or more children are called internal or non-terminal nodes.
- M-ary and Binary Trees: If in a tree, each node has a specific number of children (M) appearing in a specific
order, then we have an M-ary. The simplest M-ary tree is a binary tree, which is an ordered tree consisting of
two types of nodes; external nodes with no children, and internal nodes with exactly two children called left
and right child respectively.
Branches Parent or
s root node
children
nodes
A node may contain a value, a condition or another tree on its own. The node at the top is the root, the tree's ``starting
point.'' The arcs between nodes are called branches or edges. A node that has no branches underneath it is called a
leaf.
The height of a node is the length of the longest downward path to a leaf from that node or the maximum of the levels
of the tree nodes. The height of the root is the height of the tree.
The depth of a node is the length of the path to its root (i.e., its root path).Real trees grow from their root upwards to
the sky, but computer-science trees grow from the root downwards.
The path length of a tree is the sum of the levels of all the tree’s nodes.
The internal path length of a binary tree is the sum of the levels of all the tree’s external nodes
The external path length of a binary tree is the sum of the levels of all the tree’s external nodes.
Uses of Trees
Tree transversal is the visiting of nodes in a particularly systematic order given a starting point.
- Pre-order transversal: That is visit the node, then the left sub-tree and then the right sub-tree.
Compiled by: Mr. Clive Ayumbi 75207337
4
- In-order transversal: That is visiting the left sub-tree, then the node and then the right sub-tree.
- Post-order: That is visit the sub-tree, then the 1) Find the height of the tree.
right sub-tree and then the node.
Ans 5
Consider the binary tree bellow and answer the
questions the follows. 2) Find the depth of the tree.
Ans 4
E
3) How many leaves are on the
B F tree? Ans 7
4) How many nodes are on the
A D H tree? Ans 9
5) How many edges constitute
I
C G the tree? Ans 15
6) Transverse the tree using; in-
order, post-order and pre-
order.
Pre-order: EBADCFHGI
In-order: ABCDEFGHI
Post-order: ACDBGIHFE
2.8.User defined type
This data type is defined by the user. We have three main user defined types:
2.8.1.Records: for storing multiple values of the same or different data types but represented as a single entity.
An entity is called a record and it has attributes which are called fields. For example a teacher as an entity
has the following attributes: a name, an address, a telephone number etc.
2.8.2.Enumeration: for storing a set of well ordered related values. For example
3. VARIABLES
Data is stored in a computer as a variable i.e. the computer treats every data as a variable. Since there are different
forms of data, there are also different types of variable. A variable is a location in memory where data is stored. A
variable has the following attributes:
Note: Parameters refers to the list of variables in a method (function) declaration. Arguments are the actual values
that are passed in when the method (function) is invoked. When you invoke a method, the arguments used must match
the declaration's parameters in type and order.
TYPES OF VARIABLES
We shall use Pascal and C programming languages to illustrate the application of data structures.
In Pascal In C
Answer : Boolean;
telephone: string[11] }
end;
2.1 Structured English: The English language can be used to write algorithms. The good thing with this
approach is that, the algorithms are easily understood by humans but unfortunately, not understood by the computer.
Below is an example of algorithm to find the average of a set of numbers.
2. Process
1. Terminator
Terminator: The oval shape marks the Process: The rectangle represents
start or end point of a system or task. It a single action or step in an
usually contains “start” or “end” algorithm
3. Decision 4. Input/Output
Decision: The diamond Input/Output: The parallelogram
represents a decision or represents the information entering
branching point (eg If – Else) or leaving the system, eg customer
order (Input) or product (Output)
Start
Sum = Sum + N
Count = Count + 1
6. Flow Lines
Are they Yes
Flow Lines: The arrow indicates a sequence of
more N?
steps and the direction of floe
No
Average = Sum/Count End
Examples
Begin
Writeln(“This is Cameroon”)
Writeln(“I love very much”)
Writeln(“What about you?”)
End.
b) Selection Construct or Conditional construct or Choice construct
This is a group of instructions designed in such a way as to permit the computer to make choice. The choice is
based on a condition. The computer is generally given two choices to make. One for a true and the other for a
false condition. Examples include If-Then-Else, Case and Go To statements.
If-Then-Else Statement
Activity 1
True False
Activity 2 Condition Activity 3
A loop: is a sequence of statements which is specified once but which may be carried out several times
in succession. The code "inside" the loop is obeyed a specified number of times, or once for each of a
collection of items, or until some condition is met. In a flowchart a back arrow hints the presence of a
loop.
A loop is represented by the while, for, repeat constructs in most programming languages. A loop can be
bounded or unbounded.
Unbounded loops refer to those whose number of iterations depends on the eventuality that the
termination condition is satisfied.
You must ensure that the condition for the termination of the looping must be satisfied after some
finite number of iterations, otherwise it ends up as an infinite loop. They are used in the following: ways.
Statement
We express the2 above examples
Untiliteratively
(Condition) While (Condition)
Factorial(n)
for i: = 1 to n do
begin
i = i+1
end;
display “factorial”
Consider the following example for calculating the summation, which we will denote as sum(n), meaning n +
n-1 + n-2 + n-3 + ... + 2 + 1 + 0. Hence, sum(5) = 5+4+3+2+1+0 = 15. Now we will write pseudo-code to
calculate the sum(n) using Iteration.
sum(n)
begin
i = 0;
total = 0;
begin
total = total + i;
i = i + 1;
end
return total;
begin
Function definition:
Function factorial(n)
The sum function which sums the integer from 1 to n, where n is to be given.
Function sum(n)
F3 = F2 +F3
F4 = F 3 + F 2
F5 = F 4 + F 3
Exercise: write a recursive function to generate the elements in the Fibonacci sequence.
The figure above shows the operation of INSERTION-SORT on the array A= (5, 2, 4, 6, 1, 3). Each part shows
what happens for a particular iteration with the value of j indicated. j indexes the "current card" (or number) being
inserted into the “hand”( or left side of the array). Elements to the left of A[j] that are greater than A[j] move one
position to the right, and A[j] moves into the evacuated position.
Example 2: Using insertion sort, sort the word ASORTINGEXAMPLE in ascending order.
A S O R T I N G E X A M P L E
A S O R T I N G E X A M P L E
A O S R T I N G E X A M P L E
A O R S T I N G E X A M P L E
A O R S T I N G E X A M P L E
A I O R S T N G E X A M P L E
A I N O R S T G E X A M P L E
A G I N O R S T E X A M P L E
A E G I N O R S T X A M P L E
A E G I N O R S T X A M P L E
A A E G I N O R S T X M P L E
A A E G I M N O R S T X P L E
A A E G I M N O P R S T X L E
B) Bubble Sort
A A E G I L M N O P R S T X E
A A E E G I L M N O P R S T X
A A E E G I L M N O P R S T X
Bubble Sort is an elementary sorting algorithm. It works by repeatedly exchanging adjacent elements, if
necessary. When no exchanges are required, the file (or array) is sorted. In bubble sort, we keep passing through
the file, exchanging adjacent elements that are out of order, continuing until the file is sorted. Bubble sort is
slower than insertion sort and selection sort. When the minimum element is encountered during the first pass, we
exchange it with each of the elements to its left, eventually putting it at the leftmost position.
Pseudocode for a Bubble Sort Algorithm
Begin
For i ← 1 to length [A] Do
For j ← length [A] down to i +1 Do
If A[A] < A[j-1] Then
Exchange A[j] ↔ A[j-1]
End
Example 1:
Sort the Array A= (5, 2, 4, 6, 1, 3) given above using the Bubble Sort Algorithm.
Solution
5 2 4 6 1 3 2 5 4 6 1 3
2 4 5 6 1 3 2 4 5 1 6 3
2 4 1 5 6 3 2 1 4 5 6 3
1 2 4 5 6 3 1 2 4 5 3 6
1 2 4 3 5 6 1 2 3 4 5 6
From the figure notice how the elements are interchange as one moves from left to right and top
to down.
Example 2: Using bubble sort, sort the word ASORTINGEXAMPLE in ascending order.
A S O R T I N G E X A M P L E
A A S O R T I N G E X E M P L
A A E S O R T I N G E X L M P
A A E E S O R T I N G L X M P
A A E E G S O R T I N L M X P
A A E E G I S O R T L N M P X
A A E E G I L S O R T M N P X
A A E E G I L M S O R T N P X
A A E E G I L M N S O R T P X
A A E E G I L M N O S P R T X
A A E E G I L M N O P S R T X
A A E E G I L M N O P R S T X
C) Selection sort: A A E E G I L M N O P R S T X
In selection sort we
find the smallest element in the list and
exchange it with the A A E E G I L M N O P R S T X first element, then find
the second smallest element and exchange
it with the second A A E E G I L M N O P R S T X element and the process
continuous. This is an elementary sort method
A A E E G I L M N O P R S T X
of choice for files with huge items and small
keys, because for such applications, the cost of moving data dominates the cost of making comparisons, and selection sort
involves substantially less data movement than other algorithms.
Example 1 : Using selection sort, sort the word ASORTINGEXAMPLE in ascending order.
A S O R T I N G E X A M P L E
A S O R T I N G E X A M P L E
A A O R T I N G E X S M P L E
A A E R T I N G O X S M P L E
A A E E T I N G O X S M P L R
A A E E G I N T O X S M P L R
A A E E G I N T O X S M P L R
A A E E G I L T O X S M P N R
A A E E G I L M O X S T P N R
A A E E G I L M N X S T P O R
A A E E G I L M N O S T P X R
A A E E G I L M N O P T S X R
3.2.2 Searching A A E E G I L M N O P R S X T
Algorithms
These are algorithms used in
search for the A A E E G I L M N O P R S X T present of some
specific data in a file. An example is
the sequential A A E E G I L M N O P R S T X search algorithm
wherein the target data is search
A A E E G I L M N O P R S T X
sequentially, that is, line by line
through entries of the array without skipping. When the data is found, the algorithm returns True otherwise it
returns False.
Pseudocode: Sequential Search
SEARCH(x, [A])
SEARCH ←False
For i ← 1 to length [A] Do
If A[i] = x Then
SEARCH ← True
Else
SEARCH ← False
Sequential search algorithm: To over the limitation to wasted memory for large key values, an alternative
approach is to store the items in an array contiguously. This approach presents two alternatives as to how items are added
to the array. The first is to ensure that we insert new items in their correct positions and then shift all larger items one
position backwards so that the array is always sorted. The second approach is to add all new items to the end of the array.
Binary search algorithm: Binary search algorithm is a very efficient and widely used search algorithm that is
applied to a sorted list. The algorithm uses the divide and conquer approach, that is the algorithm begins by dividing the
list into two parts and a determination is made if the key if present would be on the first or the second half. The section of
the list that can not contain the key is then discarded, and the section that the algorithm concentrates on the part that may
contain the key.
An algorithm for binary search is given as:
Get the lower and upper bounds of a sorted list that is first and last
While last > first
Compute middle of list (mid)
If mid < searched item that is search item may only be on right of list then
First = mid + 1 ( new lower bound)
Else search item may only be on left of list
Last = mid – 1
If last point to search item, search is successful otherwise, it is not.
Hashing: A process of identifying the address of a record in a character string. When the number of keys actually
stored is small relative to the total number of possible keys, hash tables become an effective alternative to directly
addressing an array, since hash tables typically use an array size proportional to the number of distinct keys actually
stored. Search algorithms that use hashing consist of two separate parts. The first part computes a hash function that
transforms the search key into a table address. If h is the hash function we say that an element with key k hashes to slot
h(k); we also say that h(k) is the hash value of key k.
Hash function: This is a simple arithmetic operation that transforms keys into table addresses. If an array has a size
m, the hash function transforms key into integers in the range
4.0 Algorithms Development Techniques
The following techniques are used in developing good algorithms.
4.1 Divide and Conquer Technique.
In this technique, the bigger problem is divided into smaller sub problems of the same type, which are
easier to solve and solve these sub problems recursively. At the end, combine the solutions to the sub
problems into a solution to the original problem.
b) Deadlock
When two algorithms are in a deadlock, if they cannot continue their execution. This happens when the
resource needed by one to complete execution, is needed also by another with the same priority.
c) Lovelock
Lovelock is a situation in which two algorithms enter an infinite loop because they keep on exchanging
resources. This usually happens when the output of algorithm A serves as input for algorithm B and vice
visa.
6.0 Testing, Evaluation and Analysis of algorithms
After an algorithm is written or designed it must be tested to ensure correctness.
6.1 Testing
Testing an algorithm is done by first performing a dry run. That is running the mentally. If it runs mentally
without any error, then it can now be tested with some sample data in a real system. An algorithm that runs
successfully to the end does not mean it is correct. It can run without errors yet solving a different problem.
6.2 Evaluation
This entails checking an algorithm to ensure that it actually solves the problem for which it was designed.
6.3 Analysis
After testing and evaluating an algorithm we might discover that, though it solves the problem for which it
was designed, it might be doing so at the expense of system resources such as memory space and processor
time. If this is the case, the algorithm is said to be working but not effective. Analysis an algorithm entails
checking to ensure correctness, reasonableness and effectiveness.
Programming
A computer program is a sequence of instructions that is used to operate a computer to produce a
specific result.
Programming is the process of writing these instructions in a language that the computer can respond to
and that other programmers can understand.
The set of instructions that can be used to construct a program is called a programming language.
On a fundamental level, all computer programs do the same thing (Figure1-1).
They direct a computer to accept data (input), to manipulate the data (process), and to produce reports
(output).
This implies that all computer programming languages must provide essentially the same capabilities for
performing these operations.
b) Assembly Languages
A low-level language is one in which instructions are written using mnemonic to be translated for the machine
to be execute. A low-level language does not need a compiler or interpreter to run. They are call low level
because they are closer to machine language.
Such languages are cumbersome for humans and simply too slow and tedious for most programmers.
Assembly languages
These are English-like abbreviations (symbols) to represent elementary operations. These abbreviations
form the basis of assembly languages. Translator programs called assemblers were developed to
convert early assembly-language programs to machine language at computer speeds.
Examples: Pascal, BASIC, COBOL, C, C++, C#, Java, FORTRAND, LISP, Prolog, Ada etc
High-Level languages versus Low-Level languages
Low level High level
Does not need a compiler Need a compiler
Difficult to understand Easier to read, write, maintain Or user friendly
Machine oriented Problem oriented
Machine dependent Portable across platform
Require less memory Require more memory because of the compiler
Programming Paradigm or LANGUAGE TOOLS
A programming paradigm is a fundamental style of computer programming. (Compare with a methodology, which is a
style of solving specific software engineering problems.) Paradigms differ in the concepts and abstractions used to
represent the elements of a program (such as objects, functions, variables, constraints, etc.) and the steps that compose
a computation (assignment, evaluation, continuations, data flows, etc.).
Translators: A translator is a program that converts statements written in one language to statements in
another language e.g converting assembly language to machine code. the assembly-language program would
be called the source program and the machine-code would be called the object program.
Interpreters convert each high level instruction into a series of machine instructions and then immediately run
(or execute) those instructions. In some cases, the interpreter has a library of routines and looks up the correct
routine from the library to handle each high level instruction.
Compilers convert a finished program (or section of a program) into object code. This is often done in steps.
Some compilers convert high level language instructions into assembly language instructions and then an
assembler is used to create the finished object code. Some compilers convert high level language instructions
into an intermediate language. This intermediate language is platform-independent (it doesn’t matter which
actual computer hardware is eventually used). The intermediate language is then converted into object code for
a specific kind of computer. This approach makes it easier to move (or port) a compiler from one kind of
computer to another. Only the last step (or steps) need to be rewritten, while the main complier is reused.
Compiled code almost always runs faster than interpreted code. An optimizing compiler examines a high level
program and figures out ways to optimize the program so that it runs even faster.
A compiler is a computer program that translates code written in a high level language to a lower level
language, e.g. assembly language or machine language. The most common reason for translating source code is
to create an executable program (converting from a high level language into machine language).
Advantages of using a compiler
• Source code cannot be stolen/copied
• Tends to be faster than interpreting source code
• Produces an executable file, and therefore the program can be run without need of the source code
Disadvantages of using a compiler
• Object code needs to be produced before a final executable file
• The source code must be 100% correct for the executable file to be produced
An assembler translates assembly language into machine language. Assembly language consists of mnemonics
for machine opcodes so assemblers perform a 1:1 translation from mnemonic to a direct instruction.
Conversely, one instruction in a high level language will translate to one or more instructions at machine level.
Advantages of using an assembler
• Assembly code is often very efficient (and therefore fast) because it is a low level language
• It is fairly easy to understand due to the use of English-like mnemonics
Disadvantages of using an assembler
• Lots of assembly code is needed to do relatively simple tasks, and complex programs require lots of
programming time
• Assembly tends to be optimised for the hardware it's designed for, meaning it is often incompatible with
different hardware
Linkers
As programs grow in size, requiring teams of programmers, there is a need to break them up into separate files
so that different team members can work on their individual assignments without interfering with the work of
others. Each file is compiled separately and then combined later. Linkers are programs that combine the
various parts of a large program into a single object program.
A loader is a program that loads programs into main memory so that they can be run. In the past, a loader
would have to be explicitly run as part of a job. In modern times the loader is hidden away in the operating
system and called automatically when needed.
Editors. An editor is a program that is used to edit (or create) the source files for programming. Editors rarely
have the advanced formatting and other features of a regular word processor, but sometimes include special
tools and features that are useful for programming.
Programming paradigms represent fundamentally different approaches to the programming process and
therefore affect the entire software development process. . The four paths are: the functional, object-oriented,
imperative, and declarative paradigms, with various languages associated with each paradigm.
A program can often be conceived simply as a list of instructions to be executed in order; that is, a procedure to
be followed by the computer. Procedural programming captures standard solutions to computational problems
in blocks of codes that can be accessed by name.
In procedural programming, the code for a specific job is contained in a named procedure. Another name for a
procedure is often subroutine. For instance, one might create a procedure to find the standard deviation of an
array of numbers.
Once you have a routine that calculates the standard deviation of an array of numbers, that routine can be used
again and again. Such reuse can be accomplished by including the routine in whatever new program one writes
or by adding the routine to a library where other programs can access the procedure by name.
Logic languages
Imperative programming language
It is a programming paradigm that describes computation in terms of statements. Imperative programs define
sequences of commands for the computer to perform i.e. define how things are done in terms of sequences of actions
to be taken. Examples of imperative programming languages are FORTRAN, BASIC and C. An imperative programming
language can be procedural or declarative.
a) Procedural programming
In this approach, emphasis is on procedures. A problem is divided into procedures and each procedure has a clearly
defined task. Examples of procedural is programming languages are Pascal, C, BASIC, and FORTRAN are procedural
programming languages
b) Declarative programming
It is a programming approach in which programs describe the desired results of the program, without explicitly listing
the steps that need to be carried out to achieve the results. In other words it describes a problem rather than defining
the solution. The focus to the programmer is what the program is doing and not how it is done. As such a programmer
can often tell, simply by looking at the names, arguments and return types of procedures including comments, what a
particular procedure is supposed to do without necessarily looking at the details.
Object-oriented design
This is a problem-solving methodology that produces a solution to a problem in terms of self-contained entities
called objects, which are composed of both data and operations that manipulate the data. Object-oriented design
focuses on the objects and their interactions within a problem. Underlying object-oriented design (OOD) are the
concepts of classes and objects.
Object Orientation
An object is an independent entity which can be treated in isolation of all other objects. Each object has an
identity which is distinct from all others. Given any pair of objects, it is always possible to determine whether
they are the same or different. An object class, or class for short is a description of a group of objects with
similar properties and behaviours.
A class contains fields that represent the properties and behaviors of the class. A field can contain data value(s)
and/or methods (subprograms). A method is a named algorithm that manipulates the data values in the object.
– Encapsulation;
– Inheritance;
– Polymorphism.
– Abstraction
Encapsulation
Encapsulation typically refers to the hiding of data and of the implementation of an object. Data and code, when
encapsulated, are hidden from external view. When an external observer views an encapsulated object, only the
exterior interface is visible; the internal details are invisible and cannot be accessed. Thus, data which is
encapsulated cannot directly be manipulated and, in particular, cannot be directly updated. The implementation
of an object should, ideally, be hidden from view.
Inheritance
Objects tend to be defined in terms of other objects. When a new object or kind of object is defined, it is defined
in terms of those properties that make it special. Because objects are frequently defined in terms of other
objects, a mechanism is present so that the properties of those objects upon which a new one depends can be
transferred to the new object from the old one. This mechanism is called inheritance. Inheritance enables
programmers to reuse the definitions of previously defined structures. This clearly reduces the amount of work
required in producing programs.
Polymorphism
The word “polymorphism” literally means “having many forms”. In programming languages, polymorphism is
most often taken to be that property of procedures by which they can accept and/or return values of more than
one type.
Abstraction:
This a technique used in programming where the complicated details of the hard ware or soft ware are hidden
from the user and allow him or her with the superficial features that can easily be used to solve his or her
problem.
8. Report-generator programming languages take a description of the data format and the report
to generate and from that they either generate the required report directly or they generate a program to
generate the report. Similarly, forms generators manage online interactions with the application system users
or generate programs to do so.