Posts by Collection

portfolio

DEI Service in CS department at Colgate University

DEI Research in CSed

At Rutgers University, my colleagues and I studied a dataset spanning over 3.5 academic years, looking at the gender gap, trends related to the gender gap, and how they impact retention in introductory courses.

Monica Babes-Vroman, Isabel Juniewicz, Bruno Lucarelli, Nicole Fox, Thu Nguyen, Andrew Tjang, Georgiana Haldeman, Ashni Mehta, and Risham Chokshi. 2017. Exploring Gender Diversity in CS at a Large Public R1 Research University. In Proceedings of the 2017 ACM SIGCSE Technical Symposium on Computer Science Education (SIGCSE ‘17). Association for Computing Machinery, New York, NY, USA, 51–56.

publications

Completed Work in Large Distributed Scientific Workflows

  • Journal Papers

    • Georgiana Haldeman, Ivan Rodero, Manish Parashar, Sabela Ramos, Eddy Z. Zhang, and Ulrich Kremer. 2015. “Exploring energy-performance-quality tradeoffs for scientific workflows with in-situ data analyses”, Computer Science - Research and Development, May 2015, pp. 207-218.

    Download paper here


  • Conference Paper

    • Mehmet Fatih Aktas, Georgiana Haldeman, and Manish Parashar. 2014. “Flexible scheduling and control of bandwidth and in-transit services for end-to-end application workflows”. In Proceedings of the Fourth International Workshop on Network-Aware Data Management (NDM ‘14). IEEE Press, 28–31.

    Download paper here

Completed Work in CSed

  • Journal Papers

    • Georgiana Haldeman, Monica Babeş-Vroman, Andrew Tjang, and Thu D. Nguyen. 2021. CSF: Formative Feedback in Autograding. ACM Trans. Comput. Educ. 21, 3, Article 21 (September 2021), 30 pages.

    Download journal paper here

  • Conference Papers

    • Georgiana Haldeman, Andrew Tjang, Monica Babeş-Vroman, Stephen Bartos *, Jay Shah *, Danielle Yucht *, and Thu D. Nguyen. 2018. Providing Meaningful Feedback for Autograding of Programming Assignments. In Proceedings of the 49th ACM Technical Symposium on Computer Science Education (SIGCSE ‘18). Association for Computing Machinery, New York, NY, USA, 278–283.

    Download conference paper here

    • Monica Babes-Vroman, Isabel Juniewicz *, Bruno Lucarelli *, Nicole Fox *, Thu Nguyen, Andrew Tjang, Georgiana Haldeman, Ashni Mehta *, and Risham Chokshi *. 2017. Exploring Gender Diversity in CS at a Large Public R1 Research University. In Proceedings of the 2017 ACM SIGCSE Technical Symposium on Computer Science Education (SIGCSE ‘17). Association for Computing Machinery, New York, NY, USA, 51–56.

    Download conference paper here

  • Posters

    • Joseph A. Boyle *, Georgiana Haldeman, Andrew Tjang, Monica Babes-Vroman, Ana Paula Centeno, and Thu D. Nguyen. 2019. Dynamic Recitation: A Student-Focused, Goal-Oriented Recitation Management Platform. In Proceedings of the 50th ACM Technical Symposium on Computer Science Education (SIGCSE ‘19). Association for Computing Machinery, New York, NY, USA, 1269.

    Download poster here

PhD Thesis

Georgiana Haldeman. Automated feedback generation for programming assignments.

  • Nguyen, Thu D (chair); Zhu, He (chair); Kremer, Ulrich (member) - Rutgers University
  • Zilles, Craig (external member) - University of Illinois

Download thesis here

Recent Work in CSed

(* indicates undergraduate student author)

  • Conference

    • Georgiana Haldeman. 2024. A synthesized teaching model that promotes both procedural and conceptual knowledge dimensions in CS1. In Proceedings of the 8th Conference on Computing Education Practice (CEP ‘24). Association for Computing Machinery, New York, NY, USA, 9–12 (4 pages).

    Download conference paper here

  • Posters

    • Georgiana Haldeman, Emma Pizer *, Mathelide Hou *, Matthew Rojas *, Kevin Han *, and Ahmed Kamran *. RAVIC: Runtime Analysis Visualizer for Introductory Courses. In Proceedings of the 54th ACM Technical Symposium on Computer Science Education V. 2 (SIGCSE 2023). Association for Computing Machinery, New York, NY, USA, 1391.

    Download poster here

Recent Presentations in CSed

  • Computing Education Practice (CEP) 2024, Durham, UK

    On January 5th, 2024, I presented the paper on the new syntesized pedagogical model I developed for CS1 in Python and CEP. See slides here

sat

Intro to SAT

Published:

Software Engineering

Common job descriptions for someone who writes code are programmer, software developer, or software engineer. They are all describing the same job. The variety in names hints at the similarities and differences between software engineering and other engineering. Engineering aims to build something that serves a function, a product, or an artifact.

Most engineering activities involve a design that is separately developed from the product. There are three inter-related main ways in which software engineering differs:

  1. It is malleable (the product can be changed without necessarily changing the design)
  2. It is human-intensive (in regular engineering, the product development involves manufacturing, most often mechanized with little to no involvement from humans)
  3. The quality of the product is more often than not intermixed with that of the design. The right process helps to produce the right product and vice versa.

In fact, it’s more common to hear about the software process vs. the software product in software engineering than design vs. product.

Software Quality

Software Analysis and Testing are very closely related to Software Quality. There are many desirable software qualities in software engineering: correctness, reliability (relative correctness), robustness (reasonable behavior even in circumstances that were not anticipated in the requirements), performance, usability (ease of use), verifiability (we will cover testing in this course), security, maintainability, reusability, portability, understandability (although some applications are less clear than others the design and implementation should not further affect it), interoperability, productivity, timeliness, visibility (transparency of the process, amenable for external examination).

When addressing software quality it’s also useful to consider the different parties: users, developers, and managers because some of the software qualities are visible to the user (we call these external qualities) and some are only visible to the developers of the system (we call these internal qualities). A user wants a product to be reliable, efficient, and easy to use. The developer wants it to be verifiable, maintainable, portable, and extensible, the manager wants the process to be productive, predictable, and easy to control.

Furthermore, while the process is for the most part not visible to the user, the product has user-visible components such as executable code and user manual, and intermediate components (work products or artifacts) such as requirements, design documents, test data, etc. These intermediate components can also be visible to other types of users (other than the end-users).

We conclude that there is a high overlap between external and internal qualities, product and process qualities which makes it hard to standardize it. There are several software quality models that share the same essential aspects but differ in emphasis and organization.

Software Quality Principles

Some general principles to achieve the software qualities discussed prior are:

  • Rigor and Formality. Rigor means precision and exactness. Formality is a stronger requirement than rigor which requires the use of mathematical laws to evaluate and proceed. It’s impossible to achieve formality in software engineering because of its creative nature so the best we can strive for in many instances is rigor. We will briefly study some formal methods in this course. However, one crucial expectation of the course is that you will practice rigor in your software engineering activities. For example, many students coming into this course are very intuitive about their testing practices. In a few weeks, we will discuss the differences between intuitive and systematic testing. We will then learn different systematic and effective methods to engage in testing. Note that although in the past you were assessed based on the end product (the tests you provided), in this course you will be primarily assessed on the rigor of the process and the reporting.
  • Separations of concerns
  • Modularity
  • Abstraction
  • Incrementality
  • Anticipation of change and generality

Code Style

Code style is connected to many desirable software qualities of code, including correctness. Let’s look at this code:

class BoardComputer {

    CruiseControl cruiseControl;

    void authorize(User user) {
        Objects.requireNonNull(user);
        if (user.isUnknown())
            cruiseControl.logUnauthorizedAccessAttempt();
        if (user.isAstronaut())
            cruiseControl.grantAccess(user);
        if (user.isCommander())
            cruiseControl.grantAccess(user);
            cruiseControl.grantAdminAccess(user);
    }
}

Note that the last line of code is not part of the body of the last if statement and in fact, gets executed every time the method authorize is invoked. For this particular application, this goes beyond correctness and presents a high security risk. The fix is to add curly braces around the body like so:

class BoardComputer {

    CruiseControl cruiseControl;

    void authorize(User user) {
        Objects.requireNonNull(user);
        if (user.isUnknown()){
            cruiseControl.logUnauthorizedAccessAttempt();
        }
        if (user.isAstronaut()){
            cruiseControl.grantAccess(user);
        }
        if (user.isCommander()){
            cruiseControl.grantAccess(user);
            cruiseControl.grantAdminAccess(user);
        }
    }
}

One may also conclude that the three cases are mutually exclusive and combine them in an if-else-if structure like so:

class BoardComputer {

    CruiseControl cruiseControl;

    void authorize(User user) {
        Objects.requireNonNull(user);
        if (user.isUnknown()){
            cruiseControl.logUnauthorizedAccessAttempt();
        }
        else if (user.isAstronaut()){
            cruiseControl.grantAccess(user);
        }
        else if (user.isCommander()){
            cruiseControl.grantAccess(user);
            cruiseControl.grantAdminAccess(user);
        }
    }
}

However, note that there is an asymmetry in the code from the fact that we are mixing authorizing code with non-authorizing code. We can increase the understandability of our code by separating them out like so:

class BoardComputer {

    CruiseControl cruiseControl;

    void authorize(User user) {
        Objects.requireNonNull(user);
        if (user.isUnknown()) {
            cruiseControl.logUnauthorizedAccessAttempt();
            return;
        }

        if (user.isAstronaut()) {
            cruiseControl.grantAccess(user);
        } else if (user.isCommander()) {
            cruiseControl.grantAccess(user);
            cruiseControl.grantAdminAccess(user);
        }
    }
} 

[Code Style Worksheet] [Answers]

Appropriate constructs

Published:

One of the examples from last class used an enum structure:

private Result analyzeOrganic(Sample sample) {
    if (microscope.isHumanoid(sample) == false) {
        return Result.ALIEN;
    } else {
        return Result.HUMANOID;
    }
}

An enumeration is a pre-defined set of values, for example:

public enum Result {
    HUMANOID, INORGANIC, ALIEN
}

The advantage to using an enumeration is that when you declare a variable, a parameter or return type of an enumeration type then all the values (stored in the variable, passed to the parameter or returned) must be in the predefined set of the enumeration. For example, analyzeOrganic can only return three values.

Additionally, you can add attributes and constructor to enumerations just like with classes likes so:

public enum Result {
    HUMANOID(75), 
    INORGANIC(40), 
    ALIEN(100);

    final int coolnessIndex;

    Result(int newCI){
        this.coolnessIndex = newCI;
    }
}

Warm-up Problem: How can we improve the quality of this code snippet:

class CruiseControl {

    private double targetSpeedKmh;

    void setPreset(int speedPreset) {
        if (speedPreset == 2) {
            setTargetSpeedKmh(16944);
        } else if (speedPreset == 1) {
            setTargetSpeedKmh(7667);
        } else if (speedPreset == 0) {
            setTargetSpeedKmh(0);
        }
    }

    void setTargetSpeedKmh(double speed) {
        targetSpeedKmh = speed;
    }
}

Numbers without an apparent meaning that steer the program are called magic numbers. We can dispel their magic and thus improve the quality of the code by making them constants:

class CruiseControl {

    static final int STOP_PRESET = 0;
    static final int PLANETARY_SPEED_PRESET = 1;
    static final int CRUISE_SPEED_PRESET = 2;

    static final double CRUISE_SPEED_KMH = 16944;
    static final double PLANETARY_SPEED_KMH = 7667;
    static final double STOP_SPEED_KMH = 0;

   private double targetSpeedKmh;

    void setPreset(int speedPreset) {
        if (speedPreset == CRUISE_SPEED_PRESET) {
            setTargetSpeedKmh(CRUISE_SPEED_KMH);
        } else if (speedPreset == PLANETARY_SPEED_PRESET) {
            setTargetSpeedKmh(PLANETARY_SPEED_KMH);
        } else if (speedPreset == STOP_PRESET) {
            setTargetSpeedKmh(STOP_SPEED_KMH);
        }
    }

    void setTargetSpeedKmh(double speed) {
        targetSpeedKmh = speed;
    }
}

Can we do better?

Note that nothing restricts the user to call setPreset with values other than the three values it checks for and the function terminates without error. We can make the semantics of this function more transperant for outside usage by using an enum construct like so:

class CruiseControl {

    private double targetSpeedKmh;

    void setPreset(SpeedPreset speedPreset) {
        Objects.requireNonNull(speedPreset);

        setTargetSpeedKmh(speedPreset.speedKmh);
    }

    void setTargetSpeedKmh(double speedKmh) {
        targetSpeedKmh = speedKmh;
    }
}
enum SpeedPreset {
    STOP(0), PLANETARY_SPEED(7667), CRUISE_SPEED(16944);

    final double speedKmh;

    SpeedPreset(double speedKmh) {
        this.speedKmh = speedKmh;
    }
}

[Appropriate Construct Worksheet] [Answers]

Conventions

Published:

Conventions are rules that are widely agreed upon by professionals. Used approapriately and consistently, they can help with the readability of the code.

Rule 1: Remove excessive comments for example:

class Inventory {
    // Fields (we only have one)
    List<Supply> supplies = new ArrayList<>(); // The list of supplies.

    // Methods
    int countContaminatedSupplies() {
        // TODO: check if field is already initialized (not null)

        int contaminatedCounter = 0; // the counter
        // No supplies => no contamination
        for (Supply supply : supplies) { // begin FOR
            if (supply.isContaminated()) {
                contaminatedCounter++; // increment counter!
            } // End IF supply is contaminated
        }// End FOR

        // Returns the number of contaminated supplies.
        return contaminatedCounter; // Handle with care!
    }
} // End of Inventory class

Becomes:

class Inventory {

    List<Supply> supplies = new ArrayList<>();

    int countContaminatedSupplies() {
        if (supplies == null || supplies.isEmpty()) {
            // No supplies => no contamination
            return 0;
        }

        int contaminatedCounter = 0;
        for (Supply supply : supplies) {
            if (supply.isContaminated()) {
                contaminatedCounter++;
            }
        }

        return contaminatedCounter;
    }
}

Rule 2: Remove code in comments for example:

class LaunchChecklist {

    List<String> checks = Arrays.asList(
            "Cabin Leak",
            // "Communication", // Do we actually want to talk to Houston?
            "Engine",
            "Hull",
            // "Rover", // We won't need it, I think...
            "OxygenTank"
            //"Supplies"
    );

    Status prepareLaunch(Commander commander) {
        for (String check : checks) {
            boolean shouldAbortTakeoff = commander.isFailing(check);
            if (shouldAbortTakeoff) {
                //System.out.println("REASON FOR ABORT: " + item);
                return Status.ABORT_TAKE_OFF;
            }
        }
        return Status.READY_FOR_TAKE_OFF;
    }
}

Becomes:

class LaunchChecklist {

    List<String> checks = Arrays.asList(
            "Cabin Leak",
            "Engine",
            "Hull",
            "OxygenTank"
    );

    Status prepareLaunch(Commander commander) {
        for (String check : checks) {
            boolean shouldAbortTakeoff = commander.isFailing(check);
            if (shouldAbortTakeoff) {
                return Status.ABORT_TAKE_OFF;
            }
        }
        return Status.READY_FOR_TAKE_OFF;
    }
}

Rule 3: Replace comments with constants for example:

enum SmallDistanceUnit {

    CENTIMETER,
    INCH;

    double getConversionRate(SmallDistanceUnit unit) {
        if (this == unit) {
            return 1; // identity conversion rate
        }

        if (this == CENTIMETER && unit == INCH) {
            return 0.393701; // one centimeter in inch
        } else {
            return 2.54; // one inch in centimeters
        }
    }
}

Becomes:

enum SmallDistanceUnit {

    CENTIMETER,
    INCH;

    static final double INCH_IN_CENTIMETERS = 2.54;
    static final double CENTIMETER_IN_INCHES = 1 / INCH_IN_CENTIMETERS;
    static final int IDENTITY = 1;


    double getConversionRate(SmallDistanceUnit unit) {
        if (this == unit) {
            return IDENTITY;
        }

        if (this == CENTIMETER && unit == INCH) {
            return CENTIMETER_IN_INCHES;
        } else {
            return INCH_IN_CENTIMETERS;
        }
    }
}

Rule 4: Replace comments with utility methods for example:

class FuelSystem {

    List<Double> tanks = new ArrayList<>();

    int getAverageTankFillingPercent() {
        double sum = 0;
        for (double tankFilling : tanks) {
            sum += tankFilling;
        }
        double averageFuel = sum / tanks.size();
        // round to integer percent
        return Math.toIntExact(Math.round(averageFuel * 100));
    }
}

Becomes:

class FuelSystem {

    List<Double> tanks = new ArrayList<>();

    int getAverageTankFillingPercent() {
        double sum = 0;
        for (double tankFilling : tanks) {
            sum += tankFilling;
        }
        double averageFuel = sum / tanks.size();
        return roundToIntegerPercent(averageFuel);
    }

    static int roundToIntegerPercent(double value) {
        return Math.toIntExact(Math.round(value * 100));
    }
}

Rule 5: Do document implementation decisions using a pattern like this:

/*
* In the context of [USE CASE],
* facing [CONCERN]
* we decided for [OPTION]
* to achieve [QUALITY],
* accepting [DOWNSIDE].
*/

For example:

class Inventory {
    
    private List<Supply> list = new ArrayList<>();

    void add(Supply supply) {
        list.add(supply);
        Collections.sort(list);
    }

    boolean isInStock(String name) {
        // fast implementation
        return Collections.binarySearch(list, new Supply(name)) != -1;
    }
}

Becomes:

    boolean isInStock(String name) {
        /*
         * In the context of checking availability of supplies by name,
         * facing severe performance issues with >1000 supplies
         * we decided to use the binary search algorithm
         * to achieve item retrieval within 1 second,
         * accepting that we must keep the supplies sorted.
         */
        return Collections.binarySearch(list, new Supply(name)) != -1;
    }

Rule 6: Use examples in your documentation for instance:

class Supply {

    /**
     * The code universally identifies a supply.
     *
     * It follows a strict format, beginning with an S (for supply), followed
     * by a five digit inventory number. Next comes a backslash that
     * separates the country code from the preceding inventory number. This
     * country code must be exactly two capital letters standing for one of
     * the participating nations (US, EU, RU, CN). After that follows a dot
     * and the actual name of the supply in lowercase letters.
     */
    static final Pattern CODE =
            Pattern.compile("^S\\d{5}\\\\(US|EU|RU|CN)\\.[a-z]+$");
}

Becomes:

class Supply {

    /**
     * The expression universally identifies a supply code.
     *
     * Format: "S<inventory-number>\<COUNTRY-CODE>.<name>"
     *
     * Valid examples: "S12345\US.pasta", "S08342\CN.wrench",
     * "S88888\EU.laptop", "S12233\RU.brush"
     *
     * Invalid examples:
     * "R12345\RU.fuel"      (Resource, not supply)
     * "S1234\US.light"      (Need five digits)
     * "S01234\AI.coconut"   (Wrong country code. Use US, EU, RU, or CN)
     * " S88888\EU.laptop "  (Trailing whitespaces)
    */
    static final Pattern SUPPLY_CODE =
            Pattern.compile("^S\\d{5}\\\\(US|EU|RU|CN)\\.[a-z]+$");
}

[Naming Conventions Worksheet] [Answers]

How to address things going wrong

Published:

  1. Check for the type before casting, for example:
class Network {

    ObjectInputStream inputStream;
    InterCom interCom;

    void listen() throws IOException, ClassNotFoundException {
        while (true) {
            Object signal = inputStream.readObject();
            CrewMessage crewMessage = (CrewMessage) signal;
            interCom.broadcast(crewMessage);
        }
    }
}

With this improvement becomes:

class Network {

    ObjectInputStream inputStream;
    InterCom interCom;

    void listen() throws IOException, ClassNotFoundException {
        while (true) {
            Object signal = inputStream.readObject();
            if (signal instanceof CrewMessage) {
                CrewMessage crewMessage = (CrewMessage) signal;
                interCom.broadcast(crewMessage);
            }
        }
    }
}

Exceptions

  1. Fail Fast: Separate normal paths from validation paths, for example:
class CruiseControl {
    static final double SPEED_OF_LIGHT_KMH = 1079252850;
    static final double SPEED_LIMIT = SPEED_OF_LIGHT_KMH;

    private double targetSpeedKmh;

    void setTargetSpeedKmh(double speedKmh) {
        if (speedKmh < 0) {
            throw new IllegalArgumentException();
        } else if (speedKmh <= SPEED_LIMIT) {
            targetSpeedKmh = speedKmh;
        } else {
            throw new IllegalArgumentException();
        }
    }
}

Becomes:

class CruiseControl {
    static final double SPEED_OF_LIGHT_KMH = 1079252850;
    static final double SPEED_LIMIT = SPEED_OF_LIGHT_KMH;

    private double targetSpeedKmh;

    void setTargetSpeedKmh(double speedKmh) {
        if (speedKmh < 0 || speedKmh > SPEED_LIMIT) {
            throw new IllegalArgumentException();
        }

        targetSpeedKmh = speedKmh;
    }
}
  1. Use the type of the exception to learn more about what went wrong and communicate that in the exception message, for example:
class TransmissionParser {
    static Transmission parse(String rawMessage) {
        if (rawMessage != null
                && rawMessage.length() != Transmission.MESSAGE_LENGTH) {
            throw new IllegalArgumentException("Bad message received!");
        }

        String rawId = rawMessage.substring(0, Transmission.ID_LENGTH);
        String rawContent = rawMessage.substring(Transmission.ID_LENGTH);
        try {
            int id = Integer.parseInt(rawId);
            String content = rawContent.trim();
            return new Transmission(id, content);
        } catch (Exception e) {
            throw new IllegalArgumentException("Bad message received!");
        }
    }
}

The exception type in catch is too general, we can first make it more specific:

class TransmissionParser {
    static Transmission parse(String rawMessage) {
        if (rawMessage != null &&
                rawMessage.length() != Transmission.MESSAGE_LENGTH) {
            throw new IllegalArgumentException("Bad message received!");
        }

        String rawId = rawMessage.substring(0, Transmission.ID_LENGTH);
        String rawContent = rawMessage.substring(Transmission.ID_LENGTH);
        try {
            int id = Integer.parseInt(rawId);
            String content = rawContent.trim();
            return new Transmission(id, content);
        } catch (NumberFormatException e) {
            throw new IllegalArgumentException("Bad message received!");
        }
    }
}

We can communicate this information further using the message in the exception like so:

class TransmissionParser {
    static Transmission parse(String rawMessage) {
        if (rawMessage != null
                && rawMessage.length() != Transmission.MESSAGE_LENGTH) {
            throw new IllegalArgumentException(
                String.format("Expected %d, but got %d characters in '%s'",
                    Transmission.MESSAGE_LENGTH, rawMessage.length(),
                    rawMessage));
        }

        String rawId = rawMessage.substring(0, Transmission.ID_LENGTH);
        String rawContent = rawMessage.substring(Transmission.ID_LENGTH);
        try {
            int id = Integer.parseInt(rawId);
            String content = rawContent.trim();
            return new Transmission(id, content);
        } catch (NumberFormatException e) {
            throw new IllegalArgumentException(
                String.format("Expected number, but got '%s' in '%s'",
                        rawId, rawMessage));
        }
    }
}

You may also pass the cause chain to the exception like so:

class TransmissionParser {
    static Transmission parse(String rawMessage) {
        if (rawMessage != null
                && rawMessage.length() != Transmission.MESSAGE_LENGTH) {
            throw new IllegalArgumentException(
                String.format("Expected %d, but got %d characters in '%s'",
                        Transmission.MESSAGE_LENGTH, rawMessage.length(),
                        rawMessage));
        }

        String rawId = rawMessage.substring(0, Transmission.ID_LENGTH);
        String rawContent = rawMessage.substring(Transmission.ID_LENGTH);
        try {
            int id = Integer.parseInt(rawId);
            String content = rawContent.trim();
            return new Transmission(id, content);
        } catch (NumberFormatException e) {
            throw new IllegalArgumentException(
                String.format("Expected number, but got '%s' in '%s'",
                    rawId, rawMessage), e);
        }
    }
}

[Worksheet]

talks

Talk 1 on Relevant Topic in Your Field

Published:

This is a description of your talk, which is a markdown files that can be all markdown-ified like any other post. Yay markdown!

Tutorial 1 on Relevant Topic in Your Field

Published:

More information here

This is a description of your tutorial, note the different field in type. This is a markdown files that can be all markdown-ified like any other post. Yay markdown!

Talk 2 on Relevant Topic in Your Field

Published:

More information here

This is a description of your talk, which is a markdown files that can be all markdown-ified like any other post. Yay markdown!

Conference Proceeding talk 3 on Relevant Topic in Your Field

Published:

This is a description of your conference proceedings talk, note the different field in type. You can put anything in this field.

teaching

Rutgers University, a R1-university

Lecturer

  • Introduction to Computer Science (CS 111)
    • Spring 2018

Teaching Assistant (TA)

  • Internet Technology (CS 352)
    • Fall 2020
  • Data Structures (CS 112)
    • Summer 2016
    • Spring 2017

Colgate University, a liberal-arts institution

  • Intro to Computing I (COSC 101)
    • Spring 2024
    • Spring & Fall 2023
    • Spring 2022
    • Fall 2021
  • Software Analysis and Testing (COSC 480)
    • Fall 2023
  • Introduction to Cryptography (COSC 480)
    • Spring 2023
    • Fall 2022
  • Intro to Computing II (COSC 102)
    • Fall 2022
    • Spring 2022