Frequently Asked Questions

Index to Questions
Asterisks and Ampersands
I'm still not clear on when to use a * or an & for pointers in C. Can you clear this up?
When you have a pointer p and you want the variable it points to, use *p.

When you have a variable x and you want a pointer to it, use &x.

When you are declaring a variable p, and you want that variable to be a pointer, use *p.

    int *pInt;   /* declares pointer pInt to variable of type int */
    int *pChar;  /* declares pointer pChar to variable of type char */

Generally, &x will occur in argument-lists in invocations and *p will appear in assignment statements and declarations of parameters in functions.

warning: return makes integer from pointer without a cast
I'm getting an error that I don't know what to do about. The error message is:
    kennedy% gcc -ansi -c date.c
    date.c: In function `f':
    date.c:49: warning: return makes integer from pointer without a cast
It's referring to the following part of my code:
    char f(char month[100]){
      ... other statements ...
      return (month);
The problem is that the notion of returning strings from functions hasn't been covered in the course. (In fact, it won't be covered in this course, and you won't need it for any of the projects). You can fix the problem one of two ways:
(1) Declare f like this: void f(char month[100]), and don't put a return statement in the function.
(2) Or declare it like this: char *f(char month[100]), and put in a return statement like the one you're using now (observe the asterisk preceding the name of the function).

The second form would follow the examples in the stringExtras module. But, in either case, don't try to invoke the function on the right-hand-side of an assignment statement. Simply use the function as a command, by itself, in the same way that you have been using the strcpy and strcat functions.

Example invocations:

    char x[100];
    strcpy(x, "some stuff");
    strcat(x, " ... other stuff ...");

Multi-Word Cities in Project 9A
The String Abstraction in C
Can the name of the city be more than one word in Project 9A?
Yes, the name of the city may contain several words. However, the comma separating the city from the state will definitely be present.

Using the stringExtras module
I've downloaded the stringExtras module (files stringExtras.c, stringExtras.h, and stringExtrasPrivate.h) and imported it into my program (#include "stringExtras.h"), but it doesn't work. What gives?
Whoops! I forgot to tell you how to compile and run programs that use modules. Here's what you do:
    gcc -ansi -c stringExtras.c
    gcc -ansi -c yourProgram.c
    gcc -lm yourProgram.o stringExtras.o 

You only need to compile the stringExtras module once (gcc -ansi -c stringExtras.c), no matter how many times you recompile your program. But, you'll need to to the link step (gcc -lm yourProg...) every time you recompile your program.

The explanation under the "compile and run" link in the style guide for this project went to the wrong place. That's been fixed now. Also, I've updated the explanation in the Project 9 write-up on the web of the string functions (from the intrinsic string module and from the supplied module stringExtras) to make it more clear how to use the functions from the stringExtras module.

Offlimits Features of C
Why can't we use C operators like ++ and +=? I find them very useful.
C has oodles of useful operators, many of which will not be introduced in this course, even when they are applicable to the projects. I will not introduce the ++ operator or `op`= style assignments because I believe students are better served sticking with a few ways to do things, not several different ways to do the same thing. That way they can devote more of their attention to the concepts the course focusses on.

On Avoiding Problems By Being Careful
Programming isn't really that hard. You just need to be careful about what you're doing.
The admonition from one programmer to another that "you just have to be careful" is just one of a great many conceits common to the breed. What the software discipline needs are methods and tools that relieve from programmers as many burdensome details as possible, so that they can turn their attention to more important issues. They will still need to think carefully, but about different things.

Tom Wolfe, in his book on astronauts (The Right Stuff), points out a similar conceit in fighter pilots. Many of the early jet fighters had landing speeds that were too high for safe operation. There were lots of crashes, and the pilots who hadn't (yet) crashed comforted themselves with the belief that it wouldn't happen to them if they were just careful. Turns out, the luck element was a little higher than they allowed themselves to think.

Attending to Efficiency
You're always talking about software correctness in this course. Why don't you talk about efficiency? After all, the modern software world is very competitive. If users have to wait on their computer, they quickly become bored with it and find somebody else's product to run.
Very competitive is probably an understatement, and time-to-market is the watchword: Get it working, then optimize where necessary (and only where necessary), and deliver. To do this, use tools that handle as many details as possible automatically. The focus in this course is the efficient use software developers, not of computers.

The time of people using the software is even more important, of course, since (one hopes) there will be more of them than were on the team that developed the software. But, programmers almost never know in advance which details of their codes need to be efficient. After they've gotten the code working properly, they can use tools such as time and memory profilers to find out which components to optimize.

This course focuses on what programmers need to do first. Subsequent courses will address efficiency issues.

Memory Management
I haven't had much difficulty solving memory management problems, as long as I've had the right tools for the right job, such as good debuggers.
Memory management issues are among the toughest problems in the business. Tools are needed, but debuggers are not the right tools. Garbage collectors and tools to analyze where space is being used are a much better start.

Debugging versus Code Inspection
I've tried to follow your suggestion of studying my code to find my errors, but I've found it's a lot quicker to just get into the debugger and find out what's going on. That's what works for me.
If you continue with this habit, you will be part of the problem in the software industry. Work on some new ones, and become part of the solution. Removing the symptom of an error often does not remove the error. Generally, the use of debuggers to remove errors, instead of code inspection, is about ten times less efficient than code inspection. There is statistical evidence for this. Your experience is anecdotal, and it concentrates on local problems in the code, not overall correctness.

If there is a role for debuggers, it is in verifying hypotheses, formed during code inspection, about the defects that trigger observed symptoms of errors. Debuggers cannot effectively take the place of code inspection, regardless of your experience.

The String Abstraction in C
I'd like to use array operations in Project 9. Is that OK? After all, strings are just arrays, anyway.
Character strings are not arrays. They are finite sequences of characters. It is unfortunate that the definition of the C language reveals that arrays underly the implementation of the string abstraction. Revealing this structure leads to the sorts of abuses that you propose to use in your program.

Arrays are an important aspect of C and will be covered in this course, along with appropriate examples of their use. Project 9 provides no such examples.

I require that programs submitted in completion of Project 9 remain independent of the particular implementation of the string abstraction that C has chosen. To do this, you must use string functions to analyze strings, and you must avoid the use of array operations, such as subscripting.

Arrays are usually defined as sequences whose lengths are fixed at the time of declaration. This makes the array abstraction a poor choice for implementing strings, or for any type of sequence demanding operations such as concatenation, insertion, and deletion that lead to flux in the length dimension over the course of the computation.

The designers of C chose to represent strings as arrays almost three decades ago. Probably, they made this choice because the array representation uses memory economically. They made their decision at a time when fast-access memory, even on the most advanced computers, ran at a few megahertz and when that memory, even on the cheapest systems, cost tens of thousands of dollars per megabyte. (Actually, you couldn't really get memory by the megabyte -- it came by the kilobyte at that time.)

So, it was crucial in those days to use memory with great care. Software people spent much of their time (well over half, I believe), figuring out how to conserve it in their programs, or getting around the problems caused by their attempts to conserve it. Unfortunately, these habits persist to the present day, when they are rarely justified on an economic basis. When you take your first professional position in computer science, they will be justified even less often. For this reason, you will be better rewarded for learning well the principles of abstraction on which good software is constructed than for fiddling around with memory conservation excersises.

There are thousands of people in the programming business whose level of understanding of the principles of computer science does not encompass these principles. They hack out defect-ridden, unmaintainable software with alacrity.

The university is attempting to educate a new generation of computer scientists who appreciate the difficulty and importance of producing well-organized software grounded those few principles that have come to light in the short history of the discipline. I hope that you will be a literate and contributing member of that new generation.

Invoking Functions In the stringExtras module
In the stringExtras module (Supplied Software) some function names are preceded by an asterisk. Does the asterisk appear in an invocation?
Don't include the asterisks in the invocation.
   const char s[] = "some string";
   char t[100];
   strcpy(t, s); /* at this point, t[] is "some string" */
   strcap(t);    /* at this point, t[] is "SOME STRING" */

Base of Logarithm
Is there a way to call the value of "e" from math.h? I ask because the "log" function defined in math.h is interpreted to mean "ln" (natural log).
Yes, you can compute e via the function exp: e = exp(1.0)

But, there is no reason to compute e in this problem. It doesn't matter what log base you use, as long as you use the same base to compute constants that you use in the model that estimates time (after you plug in the constants).

The Mysterious Newline Error Message from the C compiler
The C compiler keeps sending me a strange message:
error: file does not end in newline
What does this mean?
You must be using a PC editor (such as notepad) to create your .c file. PC files are not compatible with Unix files; they use different methods of terminating lines.

To fix your PC file so it will work on Unix, get into Hugs and load the removeBS.hs program (from Supplied Software on the web site). The enter Hugs command

It will ask you for the name of a file. Enter the name of the file that you created on a PC editor. When the Hugs command is finished, exit Hugs and copy the file with the name identical to the one you entered, but with the extra extension .cln back to the file name you want to use for your program:
cp yourProgram.c.cln yourProgram.c

Now you should be able to run the C compiler against yourProgram.c

Retrieving Data From Keyboard Not Called For in Project 7
I am assuming that Project 7 means in its explanation that the user enters a value equal to or less than $1.00. Is this not so? How can we ask the user for that information if we are not allowed the scanf function yet?
Project 7 does not ask you to retrieve data from the keyboard.

Prompting for Components Separately - OK in Project 6
You say that we can prompt individually for the city, state, and zip. May we do the same for the month, day, and then year? And the size, color, etc. on the item entry?
Yes, you it is best to prompt separtely for each component of all of the entries. That simplies the checking the program must do, and can also make it easier for the person at the keyboard.

Data file for Team Project C Corrected
What happened to the dates in the purchase records in demoC.dat, the data file for Team Project C?
They were mistakenly left out. The file has been corrected as of 9:00am, Monday, October 21.

Software Should Make It Easy for People to Enter Data
In my opinion the guidelines on how to enter the address should be shown in directions when the program is run. If the user can't fiqure out how to use such a simple program then that person is too stupid to be allowed to even look at a computer.
You've touched on one of my hot-buttons, here. Software designers too often constrain the forms of data their software recognizes. This puts a burden on the people using the software: they must conform to arcane rules made up by some computer nerd with an intolerant attitude about how other people should behave. In recent years, this practice has subsided to some extent. I would like to see it eradicated. I will not gladly tolerate it in students attending my courses.

Software designers should transfer from people to computers as much as possible of the work required to carry out the task at hand. Computers should serve people, not the other way around. The job of the software designer is to make computers into effective helpers, or even obsequious servants if that pleases the people using the software. This is especially important if the people using the software have the misfortune to be stupid, which most people aren't, of course. Software designers certainly must not allege stupidity in other people as a smokescreen to hide laziness in the software development process.

Project 5 Tuples REQUIRED
What do you mean in Project 5 when you talk about converting from a string to a tuple and then from a tuple to a string.
You must define a function whose input is a string (of one of the forms described in the project as a potential form of keyboard entry) and convert it to a tuple containing the separate components of the information in the string. Then define a separate function that takes such a tuple as its argument and delivers a string in standard form.

Project 5 Input: One String, Not Several
When I prompt for the keyboard input, am I allowed to ask for the each part separately (for example, ask for the city, then for the state, then for the zip code) or must I do it all at once and have my data be only one string instead of three?
All at once - one string.

Variations in Project 5
In the portion of the project description when you mention some of the variations of the forms: every time you use the phrase "may" in your description, you mean that the variation is something that they can input and my program has to deal with, correct?
Right. Your program has to deal with all of the variations.

Handling Abbreviations in Project 5
I know that I'll be using definitions with alternatives in them, but do I have to have separate alternatives, say for Okla instead of OK or Calif instead of CA, or is there a way to make it do it all at once?
There is a way to handle all the abbreviations (and non-abbreviations, for that matter) with one mechanism.

Two Digit Years Only in Numeric Dates in Project 5C
Can a standard, military, or modern date have a two-digit year?
No. Standard, military, and modern dates will have fully specified years. Only numeric dates will have two-digit years, and you can assume that numeric dates refer to the twentieth century.

What Arbitrary Capitalization Means in Project 5
What do you mean by "all capital letters, all lower case, or any combination" in an entry?
I mean, for example, that there are eight ways that the word "red" might be entered: red, reD, rEd, rED, Red, ReD, REd, and RED. There are 64 ways to enter "orange", 256 ways to enter "December", and 50 ways to leave your lover. Your program will convert all of these to a standard form. If you try to do this with alternative formulas for every case, you'll end up with a very long program. Better think of a more organized approach.

INTERNAL ERROR: findQualTycon2
What does Hugs mean when it reports "IINTERNAL ERROR: findQualTycon2"?
I don't know, exactly, but every time I've gotten this message, there has been a function listed in the export list of a module without a definition for the function in the body of the module.

Reorienting the Picture
What do you mean "reorient the picture" in Individual Project 4D?
I guess the reorientation is a bit confusing. The picture is constructed with small values of y corresponding to strings near the beginning and large values at the end. So, if you were to print it down the page, you'd have y increasing as you go down. You want y to decrease as you go down (that is, to increase as you go up - this is the normal orientation for charts based on Cartesian coordinates).

You might ask why the picture was constructed this way in the first place: turns out its easier to build it upside down, less complicated counting.

Typo in Individual Project 4D Description
Hey! In the write-up on Project 4D you say to that one of the arguments of displayChart is the tuple delivered by the function makeChart from Project 4C. But, the function makeChart is from Project 4B. What do you mean, anyway?
Right you are! It should say "makeChart from Project 4B." The function makeChart delivers a tuple containing a picture and the ranges of the coordinates of the points plotted in the picture. This same sort of tuple will be supplied as an argument to displayChart, and displayChart is supposed to deliver a string that, if displayed on the screen, would depict that information.

Pain In Program Construction
I have been looking at the assignment for a while and am having trouble getting started. I just don't know what to do at this point. Can you help?
Not really. But, I can offer some reassurances. What you are experiencing is what all people experience when they are learning to write computer programs. In fact, the pain never really goes away. The problems just get harder. Programming is a tough, demanding, intellectural discipline. How's that for reassurance!

Programming in its purest form (and you are learning it in its purest form) involves a lot of deep thought and, for most problems, a few crucial insights. It is not mechanical. It is a creative process that has more in common with, say, proofs in geometry or factorization in algebra than with routine grinding such as long division in arithmetic or finding derivatives in calculus.

Then, when you get a leg up on the creative process and put together a draft of your program, you can start the easy part: fighting your way through all those worthless error messages -- very frustrating, but not as scary as the creative part. Creative work is always scary because you never know when the insight will come. It's a real kick when it does, though. Bon appetit!

What Error Messages Really Mean
That stupid Haskell system keeps giving me error messages that have nothing to do with my program! This is aggravating beyond belief !!!
Error messages are rarely helpful. This is true with all computing systems. Figuring out what someone had in mind when entering an incorrect command to a computer system is a very hard problem. Basically, you should interpret all error messages from computer systems as if they said this:
  Dear person,
    The input you have given me is not in a form that my
    designer has made it possible for me to understand.
    I wish I could tell you exactly what is wrong with it,
    but I really don't know. About all I can do effectively
    is process input that my designer anticipated. Outside of
    that territory, I get lost fast. I'm truly sorry about
    this state of affairs, but I'm just too ignorant to do
    any better. I hope you will be able to transform your
    input into something that I can recognize and help you
    with. I truly want to help, but I just don't know how.
                            Best wishes for a happy future,
                                Your humble computer system
In other words, all the error message tells you is that there is something wrong. You'll have to dig in and figure out what it is.

What "Unresolved top-level overloading" Means
When I try to define a sequence like this:
ns = [1 .. 50]
Haskell gives me a weird error message talking about overloading and binding and outstanding contexts and stuff like that. What can it possibly mean?
This is one place where polymorphism bites you. In the definition n = [1 .. 50], Haskell cannot figure out what kind of numbers you want to use. (There are eight numeric types to choose from.) So, it gives up. What you need to do is include a type specification with the definition (which you are supposed to do anyway - it's a new requirement in the Project Style Guide. Like this, for example:
What NaN and Inf Mean
My program for the cosine project is correct, but doesn't work. The machine spews some gobbledy-gook about NaN and Inf. What does that mean?
NaN means "not a number" and Inf means "infinity." The computer would deliver the value Inf if you tried to compute product[1.0 .. 100.0] (the factorial of 100) or pi^100, for example. It would deliver the value NaN if you tried to compute the quotient of these two quantities. The problem is that few computers provide a floating point number system that can deal with numbers this large. So, you won't be able to do the cosine project using formulas like
x^n/product[1.0 .. fromIntegral n] <-- DON'T DO THIS!
for the terms (where x^n stands for x to the n-th power).

You could use the Haskell type Rational to address the problem, but I don't recommend that approach. The problem is not with the terms themselves. The quotient x^n/n! is small enough for the floating point system on your computer. It's just that the numerator and denominator are too big, so you can't compute the quotient in the ordinary, straightforward way.

You will need to do some algebra on x^n/n!, to find a way to compute that value without getting intermediate results in the stratosphere.

What n! means
What do you mean by n! ? Haskell doesn't seem to know about that.
The factorial of a positive, integer number is the product of the integers between 1 and the number, inclusive (0! is 1, a special case). The usual notation in mathematics for the factorial of a number n is n! (the exclamation point indicates the factorial operation). Haskell does not have an intrinsic factorial operation. It wouldn't do much good anyway, because the numbers get too big, too fast.

How to Use the Framework Script
Our team decided we don't know how to implement the framework you gave us. All of our functions work, but we couldn't implement them on McCarthy.txt. All of our functions work, but they won't work on the McCarthy string. Are we even going about this problem correctly?
Probably not. Haskell is a functional language, and you probably have a conventional programming model in mind. You will have to learn to think in terms of formulas and equations instead of steps in a recipe. For example, consider this: it doesn't matter what order the definitions are written in. You can put the definition of the displayDictionSignature function first, then the defintion of the function for Project 2A, then 2B, etc., or you can put 2D first, then 2A, then displayDictionSignature, then 2E, etc. Any order - doesn't matter. If this surprises you, then you are thinking conventionally.

In this particular case, your difficulties probably involve a misunderstanding of the specificationss for the display function. It must be a function with one argument, a string, and it must deliver a string as its value. And, you must insert your definition of the display function and all the other functions you define (that is, 2A-2E + others you define) in the framework script from Supplied Software.

So, in the framework script, where it says

   your definition(s) go here
you replace those lines with the definitions of the functions you have constructed.

One way to test to see if you're anywhere close to the right idea is to test it out by using the definition

> displayDictionSignature textString = textString
intead of the one you've put together. This definition has the right type: if it is supplied a string as an argument, it will deliver a string as a value - not the right string for this problem, but at least something that will let you gain confidence that you understand how to use the framework script.

Unexpected Semicolon - Required Indentation in Scripts
I'm getting an error message that says my script contains an unexpected semicolon. But, my script doesn't have any semicolons in it. What gives?
All definitions must have exactly the same amount of space between the ">" that starts the line and the first letter of the entity being defined.


> variableOne = ...
> functionTwo xs = ...
> variableOne = ...
>functionTwo xs = ...
If a particular definition covers more than one line (and some of your definitions will), then all lines beyond the first line a the multi-line definition must be indented beyond the first line.


> displayDictionSignature textString =
>   second line starts here
>   third line starts here (or could be indented further)
>   fourth line starts here (could be indented more or
>     less than third line, but must be indented further
>     than first line of definition)
> next definition starts at same indentation as other first-lines ...

The reason for this is that Haskell uses indentation level to mark the beginning and end of definitions. Older languages use special markings for this purpose, such as matching brackets ({...} or begin...end) or separators (usually semicolons). But, good programmers always use indentation to visually delineate program segments. Bracket and separator marks are redundant when programs are properly indented. So, Haskell avoids the visual clutter of bracket and separator marks by relying solely on indentation.

Now, about those semicolons ...
Haskell has an alternate source form that uses semicolons as separators. This form is more compact, so Hugs translates what you write into the compact form (with semicolons) before it interprets your program. When your script is indented improperly, extra semicolons get inserted in places where they unexpected. You should interpret the "unexpected semicolon" error message as an indication that your indentation is messed up at some point.

Deciding What Things to Parameterize
Should functions be fed different arguments, or is it acceptable to use "hardcoded" arguments?
Functions should be parameterized with respect to those aspects that need to vary from one invocation to the next. Things that stay constant should not be parameterized. This is the essence of abstraction - one of the most important ideas in computer science.

Strictly speaking, there is no such thing as a hardcoded argument. Either there is a parameter to allow variation or there isn't. The question makes me suspect you that you are trying to do some things that won't work, such as associating the contents of the file, or some transformation of its contents, with a name that you will use elsewhere in your script.

This won't work. You must write a function called displayDictionSignature that has one parameter. That parameter will be a string, and the framework script provided for Team Project A invokes the function displayDictionSignature with an argument that is associated with the contents of the file McCarthy.txt. Those contents are represented as a Haskell String. You have no choice about this.

Small Functions or Big Ones?
Is it preferable to make "big" functions, or to make several small ones?
Small functions are best. Use the common patterns of computation (especially composition, but also mapping and folding - and, later, iteration) to glue small functions together into bigger ones.

Importance of Software Reuse
Why not just write the script for Team Project 2A from scratch? That way, we could customize the functions to fit the exact needs of the problem.
Use of existing, tested software components in new software can save a great deal of time and improve the reliability of software products. Programmers often say it's inconvenient, and, anyway, they can write better software than that old stuff laying around. So, proven components are often discarded in favor of spiffy new, unproven components. One of the goals of this course is to inculcate an appreciation of the concept of software reuse. Or, at least, to get students to try it a few times.

You dirty rat! You took a whole letter grade off just because I didn't annotate the demontrations of my function's performance in the script. How could you do that? I couldn't figure out what you wanted anyway, what with the instructions scattered all over the accursed World Wide Web.
Any suggestions you have that would help me make project requirements more clear will be gratefully received.

Use of Multiple Guards Not Allowed in Project 2
Does Haskell admit multiple guards in list comprehensions?
Yes, but that feature cannot be used in Project 2 because it isn't discussed in Lessons 1-6.

Function Definition Doesn't Work
I defined a function like this:
def remove x xs =[y | y <- xs, c /= x]

When I load it has a script there are no errors. But, when I run the function it returns the error "remove not a defined variable." That doesn't make any sense, does it?
The problem is the "def" that precedes "matchingElements". I've been using "def>" in class to indicate lines containing definitions, "cmd>" to indicate commands, etc. That probably led you to believe that you needed to introduce definitions with "def". But,you don't.

In fact, you can't. When you write

def f x = ... some formula goes here ...

you are defining a function named def with parameters f and x. If you want to define a function named f with one parameter, you should write
f x = ... some formula goes here ...

So, Hugs thought you were trying to define a function named "def". Then, when you tried to use the function "remove," it didn't know what you were talking about.

Project Descriptions Too Cryptic
The project descriptions in the write-up on Individual Project 2 are so cryptic that I can't figure out what you want me to do. What is it that you want, exactly?
Whoops! There I go speaking in code again. Have a look at an example.

Where are my grades?
You said I'd be able to use the "Individual Grades" hyperlink from the CS1323 Honors Welcome Page on the web to find a summary of my grades. They don't seem to be there. Why is that?
Your grade summary should be identified by the four-digit personal code that you supplied to the instructor. If your code doesn't appear in the list, send the instructor an email note containing your code.

Not getting email from instructor? Use .forward
You said I would be getting a lot of email from you, but I haven't gotten any. Why is that?
Maybe it's going to the wrong place. Did you let the instructor know where you wanted to receive email? If not, it's going to your ECN account. To receive it where you really want it, do this:

Or, maybe the instructor recorded the wrong address. Send him a note with your address, and ask him to check.

How to Print
I have been trying to print in unix through the use of the
"lpr -P "
command and the computer repeatedly gives me an error that states
"/usr/bin/lpr: option -P not recognized".
What should I do?
Answer ~~~ Special Note: PROBLEM NOW FIXED (9/3/96). Run Hugs on alphas. ~~~
ECN is trying to fix this problem, but it may be a week or two. In the mean time, do this:
Leave the telnet terminal window open and return to it whenever you want to print something.

Redefined Variable Message
I did everything you said, but Hugs spits the following message at me:
ERROR "sequence.hs" (line 85): Attempt to redefine variable "transpose"
What gives? Is there no justice?
The problem is that you cannot run Hugs on the alphas. There is a version of Hugs there (I am trying to have it removed), but it is out of date and won't work for this semester's projects.

So, run it on an HP. You wil need to run everything this semester on an HP. You can do some work on PC Hugs if you have your own computer, but the final version to be handed in must be on an HP.

Alas. There just isn't any suitable Hugs available on the alphas right now. There may be in a few weeks, though. Watch this space.

Beware! Don't accidently use a terminal windown on the HP in which you are running a telnet session with a DEC Alpha.

Last Modified: