2  Uses

This chapter explores several applications of AI assistance tools, from unit testing and code commenting to explaining code and writing documentation. The goal is to showcase the breadth of these tools’ capabilities, empowering you to make the most of AI in your projects.

Example prompts are provided for each use case, along with the resulting output from ChatGPT.

2.1 Memory aid

Obtain list of possible mnemonics to help memorize a concept. These could be an acronym or funny story. Alternatively, you could ask for a description to help you map the information into your memory palace. This can be helpful when you need to remember new programming concepts so you can code faster.

Give me a list of 5 memory devices to remember
$ vs ^ in regular expressions
Dollar Sign "Catcher":
Remember that the dollar sign ($) catches the end of the line in regular expressions. "Catcher" and "end" both have the letter "E" in them, which can help you remember.

"Carat Starts":
The carat symbol (^) resembles an upward-pointing arrow, pointing to the start of the line. It's like an arrow showing you the way to the beginning of the line.

Carat to Ceiling, Dollar to Down:
Associate the shape of the ^ (caret) to ceiling and $ (dollar) to down or the floor. This way you remember the caret matches the start (top/ceiling) and the dollar matches the end (bottom/down).

"$" at the End of a Price:
When we list prices, we often write the dollar sign at the end (especially in some non-English-speaking countries). So, the dollar sign can remind you of the end of the line.

Alphabetical Order:
In terms of alphabetical order, Carat comes before Dollar. This can be associated with the order in a line: Carat (^) represents the start (before), and Dollar ($) represents the end (after).

2.2 Notetaking

Markdown files are a useful file format for notetaking. They’re text files so they can be easily version controlled and shared. I personally love the Obsidian flavor of Markdown. What’s nice about the format is that you can link notes together and gain access to a large library of open-source community plugins. The key here is that you can also open the file format in VS Code and activate Github Copilot as a notetaking assistant This is very useful for autocompleting, rewriting, or summarizing notes. Alternatively, Microsoft 365 Copilot in OneNote can serve a similar role.

2.3 Transforming information

Summarize educational content into a list of bullet points or table. The content could be a video transcript or an article. Or you could go in the opposite direction and ask for a description of a table or set of bullet points. With recent updates to ChatGPT, you can now upload files such as images to summarize or use as additional context.

Here's the transcript from a Youtube video I just saw. Give me a short summary of in 4 bullet points:

We're no strangers to love
You know the rules and so do I (do I)
A full commitment's what I'm thinking of
You wouldn't get this from any other guy
I just wanna tell you how I'm feeling
Gotta make you understand
Never gonna give you up
Never gonna let you down
Never gonna run around and desert you
Never gonna make you cry
- The lyrics are from the song "Never Gonna Give You Up" by Rick Astley.
- The song talks about a person's commitment and dedication to their loved one.
- The singer emphasizes that they will never give up, let down, or hurt their partner.
- The overall message is about reassurance, trust, and the desire to make their loved one happy.

2.3.1 Generate diagrams

Visualize code, relationships or project info as a diagrams or charts in Mermaid.

Give me an accurate Mermaid flow chart for this R code: 
for (i in 1:10) {
  if (i < 3) 
    next
  print(i)
  if (i >= 5)
    break
}
graph TD
    Start(Start)
    ForLoop[For i in 1:10]
    Condition1{i < 3}
    Next[Next iteration]
    Print[Print i]
    Condition2{i >= 5}
    Break[Break loop]
    End(End)
    
    Start --> ForLoop
    ForLoop --> Condition1
    Condition1 -- Yes --> Next
    Condition1 -- No --> Print
    Print --> Condition2
    Condition2 -- Yes --> Break
    Break --> End
    Condition2 -- No --> ForLoop
    Next --> ForLoop
graph TD
    Start(Start)
    ForLoop[For i in 1:10]
    Condition1{i < 3}
    Next[Next iteration]
    Print[Print i]
    Condition2{i >= 5}
    Break[Break loop]
    End(End)
    
    Start --> ForLoop
    ForLoop --> Condition1
    Condition1 -- Yes --> Next
    Condition1 -- No --> Print
    Print --> Condition2
    Condition2 -- Yes --> Break
    Break --> End
    Condition2 -- No --> ForLoop
    Next --> ForLoop

2.4 Add emojis as visual aids into text

Ask the AI tool to include relevant emojis within the requested content. These emojis can serve as nice visual aids to written content like notes or documentation.

Steps to make a PBJ sandwich. 
Add helpful emojis into steps. 

Sure, here are the steps to make a PBJ sandwich with emojis:

1. 🥪 Gather your supcplies: two slices of bread, peanut butter, jelly, and a knife. 🍞
2. 🥜 Spread peanut butter on one slice of bread. 🥪
3. 🥜 Wash the knife. 🧼
4. 🍓 Spread jelly on the other slice of bread. 🥪
5. 🥪 Put the two slices of bread together, peanut butter and jelly sides facing each other. 🥪
6. ✂️ Cut the sandwich in half. 🥪
7. 🥪 Enjoy! 😋

2.5 Generate datasets

Ask for a dataset to be generated based on specific criteria. This can be useful for testing or educational purposes. Here’s an example dataset I used for a small icebreaker app.

Here is my current icebreakers dataset. I want you to give me 5 more in the CSV format.
question,emoji,difficulty
"What's your favorite snack?",🍿🍪,False
"What's the best candy?",🍬🍭,False
"What’s your beverage of choice?",🍹☕️,False
"What's your favorite type of weather?",⛅🌧️🌈,False
"Are you an early bird or night owl?",🐦🌙,False
"What's your zodiac sign and do you think it's accurate?",♒♏🔮,False
"If you could meet any fictional character, who would it be?",📚🎥🦸,True
"Do you play any musical instruments?",🎹🎷🎸,False

2.6 Unit testing

Provide function(s) in your prompt and ask for unit tests. This is useful for package development or data analyses involving custom functions.

Give me unit tests for the following R function using 
the testthat package: 
`
is_over_9000 <- function(x){
  stopifnot(is.numeric(x))
  x > 9000
}
`
is_over_9000 <- function(x){
  stopifnot(is.numeric(x))
  x > 9000
}
library(testthat)
test_that("is_over_9000 works correctly", {
  # Test case 1: x > 9000
  expect_true(is_over_9000(9001))
  # Test case 2: x <= 9000
  expect_false(is_over_9000(9000))
  expect_false(is_over_9000(1))
  expect_false(is_over_9000(-9001))
  # Test case 3: x is not numeric
  expect_error(is_over_9000("not_a_number"))
  expect_error(is_over_9000(NULL))
  expect_error(is_over_9000(c(1, "a")))
})

2.7 Regular expressions

Get a regular expression for pattern matching. Share an example string and the subset you would like to capture then ask for a pattern to match for it. Ensure that you create or obtain one or more unit tests to confirm it is working as expected.

Regex pattern in R that looks for function names that are 
not prefixed by package name. For example it should not 
detect `dplyr::count(iris, Species)` or 
`dplyr::glimpse(iris)`
You can use the following regex pattern in R to look for function names that are not prefixed by a package name:

`\b(?<!\w::)\w+\(`

Explanation:

\b: Matches a word boundary to ensure the match is a whole word.
(?<!\w::): Negative lookbehind to assert that there is no word character before ::, which indicates a package prefix.
\w+: Matches one or more word characters, representing the function name.
\(: Matches the opening parenthesis to complete the function call.

2.8 Commenting code

Add beacons or useful comments into existing code. Beacons are parts of a script that can help a programmer understand what the code does. These could be as a simple, one-line comment explaining a section of code or a function. Alternatively, they could be variables using words that help explain what the code is doing. This could mean variables like tree or root for a code involving a binary tree.

Add short comments after each line in the R code explaining what is going on:
flights |> 
  filter(dest == "IAH") |> 
  mutate(speed = distance / air_time * 60) |> 
  select(year:day, dep_time, carrier, flight, speed) |> 
  arrange(desc(speed))
library(nycflights13)
library(dplyr)
# Pipe operator (|>) is used to chain functions together for data manipulation.
# Filter rows where the 'dest' column is equal to "IAH".
flights |> 
  filter(dest == "IAH") |> 
# Calculate the 'speed' column by dividing 'distance' by 'air_time' and then multiplying by 60 to convert to minutes.
  mutate(speed = distance / air_time * 60) |> 
# Select specific columns 'year' to 'day', 'dep_time', 'carrier', 'flight', and the newly calculated 'speed'.
  select(year:day, dep_time, carrier, flight, speed) |> 
# Arrange the rows in descending order based on the 'speed' column.
  arrange(desc(speed))

2.9 Explain concepts

Ask to explain a new concept you are learning in a format of interest or style of interest. This is useful when there’s a lot of technical jargon or acronyms you don’t understand in a piece of text.

Explain how programmers typically use regular expressions. 
Give me a few  situations where it might be applied. 
Explain this to me in the style of Robert Dinero and keep it short.
Hey, you talkin' to me? Alright, let me break it down for you. We got these things called regular expressions, or regex for short. Think of them like a matchmaker for data. Programmers use 'em to find, replace, or manipulate strings of text in data, you follow? 

Now, I'm talking about situations like validating input fields. Yeah, like checking if an email address or phone number entered by a user is legit. Or let's say you got a huge text file, a real tome, and you need to find specific patterns or keywords in there, that's when regex comes in handy. 

Also, consider renaming a bulk of files, or parsing logs. Big headache, right? Not with regex, it's a piece of cake. These things are versatile, you get me? So next time you got a mountain of data to deal with, remember regular expressions, capisce?

2.10 Write code

Ask for help to write code and automate repetitive coding tasks. The key is to provide an accurate technical specification for the tool to work with. In other cases, you could get an example on how to do something, similar to a tutorial, then apply it to your situation.

Show me how to read a set of CSV files from a folder in R using list.files(), readr::read_csv(), and the purrr package. 
Set the element names as the csv file names. 
Note that I dont want to change the working directory and dont want to set the element names at the full file paths.
# Load required libraries
library(readr)
library(purrr)

# Specify the folder path where the CSV files are located
folder_path <- "/path/to/your/folder"

# Get a list of CSV files in the folder
csv_files <- list.files(path = folder_path, pattern = "\\.csv$", full.names = TRUE)

# Read CSV files and set element names
csv_data_list <- set_names(map(csv_files, read_csv), basename(csv_files))

# Now, csv_data_list is a named list where each element contains the data from the corresponding CSV file.
# The names of the elements are set to the CSV file names without the full file paths.

You could then ask ChatGPT to format responses into templates that you can fill in the blanks with ( (using ____ as blanks for you to edit). This is useful when you want to write code but are not sure of the exact syntax or the exact parameters to use.

2.10.1 Create simple apps

Ask for help to create simple apps. This is useful when you want to create an app but are not sure of the exact syntax or the exact arguments to use. You can generate a simple example to then start building a more complex app.

Create a simple Shiny for R app that  displays uploaded CSV data as a DT table that you can build off of
# app.R
library(shiny)
library(DT)
ui <- fluidPage(
  titlePanel("Upload CSV and Display as DT Table"),
  
  sidebarLayout(
    sidebarPanel(
      fileInput("file", "Choose CSV File",
                accept = c(
                  "text/csv",
                  "text/comma-separated-values,text/plain",
                  ".csv")
      ),
      tags$hr(),
      checkboxInput("header", "Header", TRUE)
    ),
    
    mainPanel(
      DTOutput("table")
    )
  )
)

server <- function(input, output) {
  output$table <- renderDT({
    inFile <- input$file
    
    if (is.null(inFile)) {
      return(NULL)
    }
    
    # Read the uploaded CSV
    data <- read.csv(inFile$datapath, header = input$header)
    
    # Return the datatable
    datatable(data, options = list(pageLength = 25))
  })
}

shinyApp(ui, server)

Click here to interact with the example Shiny app that displays an uploaded CSV data as a DT table.

Alternatively, you could start by uploading an image file to provide additional context to ChatGPT. Here’s an example image from the Shiny tutorial that we could use. It could also be a drawing or wireframe.

Turn this into a Shiny app in R.
# Load necessary libraries
library(shiny)
library(ggplot2)

# Define UI for application
ui <- fluidPage(
   
   # Application title
   titlePanel("Hello Shiny!"),
   
   # Sidebar with a slider input for number of bins 
   sidebarLayout(
      sidebarPanel(
         sliderInput("bins",
                     "Number of bins:",
                     min = 1,
                     max = 50,
                     value = 30)
      ),
      
      # Show a plot of the generated histogram
      mainPanel(
         plotOutput("distPlot")
      )
   )
)

# Define server logic to create histogram
server <- function(input, output) {
   
   output$distPlot <- renderPlot({
     
     # For demonstration, using a random dataset since we do not have the actual data
     x    <- rnorm(500)
     
     # Generate histogram with the requested number of bins
     bins <- seq(min(x), max(x), length.out = input$bins + 1)
     
     # Plot the histogram
     ggplot(data.frame(x), aes(x)) +
        geom_histogram(bins = input$bins, fill = 'blue', color = 'black') +
        labs(title = 'Histogram of waiting times',
             x = 'Waiting time to next eruption (in mins)',
             y = 'Frequency')
   })
}

# Run the application 
shinyApp(ui = ui, server = server)

2.11 Debug code

Get help to identify, understand, or fix problems in your code. Here it helps to define expectations.

Help me debug this R code and keep it short:
library(ggplot2)
library(palmerpenguins)
ggplot(penguins, aes(x = flipper_length_mm, y = body_mass_g)) %>%
  geom_point(aes(color = species, shape = island))
library(ggplot2)
library(palmerpenguins)

ggplot(penguins, aes(x = flipper_length_mm, y = body_mass_g)) +
  geom_point(aes(color = species, shape = island))

2.12 Explain code

Get explanations for blocks of code that you may have difficulty understanding.

Explain the following code to me. Keep it short:
stringr::str_view(words, "^[^aeiou]+$")
The code uses the stringr package in R to find and display non-vowel words in the words string. 
It uses str_view function to visually highlight (view) the non-vowel words that match the regex pattern "^[^aeiou]+$". 
The regex pattern means it will match any string that consists entirely of characters that are not vowels (i.e., only consonants).

2.13 Get feedback

Get suggestions or feedback on your code or writing. Maybe you want some ideas on how you can improve readability or performance.

Give me suggestions and feedback on this R code:

a <- 11:14
b <- 26:29
c <- numeric(length(a))
for(i in seq_along(a)) {
      c[i] <- a[i] + b[i]
}
Your R code snippet is generally well-written and should accomplish its goal of adding corresponding elements in vectors a and b and storing them in vector c. However, there are some improvements and best practices you might consider:

Suggestions:
Comments: Adding comments to your code helps improve its readability and maintainability.
Vectorization: R is optimized for vectorized operations, and using a for loop for element-wise addition is less efficient than using vectorized operations.
Pre-allocation: You've already done this by pre-allocating c using numeric(length(a)), which is good practice in R to speed up the loop.
Check for Equal Lengths: Before performing operations on two vectors, you might want to ensure they have the same length to avoid unexpected results or errors.

Improved Code:
Here's how you might rewrite your code with these considerations:

# Ensure both vectors have the same length
if(length(a) != length(b)) {
  stop("Vectors a and b must have the same length")
}

# Perform element-wise addition using vectorized operations
c <- a + b

As you can see, I've replaced the for loop with a vectorized operation (`a + b`), which is more efficient and easier to read. I've also added a check to make sure that the lengths of `a` and `b` are the same before performing the addition.

2.14 Brainstorm ideas

Get ideas for a project or a problem you are working on. This is useful when you are stuck and need some inspiration. You can use this to help you mindmap.

Give me a list of 5 possible titles for a short book on AI Assistant tools.
Certainly! Here are five possible titles for a short book on AI Assistant tools:

1. "Mastering the Virtual Assistant: A Guide to AI-Powered Tools"
2. "Speak, Command, Achieve: Unlocking the Potential of AI Assistants"
3. "AI Assistants Demystified: Your Pocket Guide to the Future"
4. "Hey Siri, Write My Book: Exploring the World of AI Assistants"
5. "Voice of the Future: How AI Assistants are Changing Our Lives"

Feel free to adjust these titles to better fit the specific angle or approach of your book!

2.15 Refactor code

Refactor your code that may be inefficient or unnecessarily complicated. For example, maybe you are defaulting to looping structures like for or while loops when you could be using vectorized solutions.

Refactor this R code to take advantage of vectorization:
x <- 1:5
y <- 10:15
output_vector <- c()
for (i in 1:length(x)) {
  output_vector[i] <- x[i] + y[i]
}
output_vector
x <- 1:5
y <- 10:15
output_vector <- x + y
output_vector

2.16 Reformat code

Rearrange code so that it is more readable and understandable.

Reformat this R code so that is more readable and follows good R conventions:
1+            1
x=1:10#some users prefer '<-' as the assignment operator
if(TRUE){
print('Hello world!') # HI!
}
# Adding two numbers
result <- 1 + 1

# Creating a sequence from 1 to 10
x <- 1:10 # some users prefer '<-' as the assignment operator

# Conditional statement with a print statement
if (TRUE) {
  print('Hello world!') # HI!
}

2.17 Reverse prompt engineering

You can an output and generating a prompt that would produce something resembling that output. This is useful if you have a reference output on hand but don’t know how to get there.

Give me a prompt to generate text resembling this persona: 
"You talkin’ to me? You talkin’ to me?"
Certainly! If you're looking to generate text in the persona of a character who famously says, "You talkin' to me? You talkin' to me?", you're likely channeling the vibe of Travis Bickle from the movie "Taxi Driver." Here's a writing prompt to help you generate text in that persona:

Writing Prompt:

You're Travis Bickle, a taxi driver in New York City. It's late at night, and you've just picked up a passenger who seems a bit sketchy. They ask you to take them to a location that's notorious for illegal activities. As you drive, you start talking to yourself, contemplating the morality of your actions and the state of the world around you. What do you say?

Feel free to use this prompt to generate text that captures the essence of this persona.

2.18 Compare text or code

Explain differences between two bodies of text or snippets of code. Here’s an example of comparing two code snippets.

Compare the following two R code snippets and explain the differences:
# Code snippet 1
x <- 1:5
y <- 10:15
output_vector <- c()
for (i in 1:length(x)) {
  output_vector[i] <- x[i] + y[i]
}
output_vector

# Code snippet 2
x <- 1:5
y <- 10:15
output_vector <- x + y
output_vector
Both code snippets aim to perform element-wise addition of two vectors `x` and `y` and store the result in `output_vector`. However, there are some notable differences:

### Code snippet 1

1. **Explicit Loop**: This snippet uses a `for` loop to iterate over each element in the vectors `x` and `y`. In each iteration, the loop adds the corresponding elements from the vectors and stores them in `output_vector`.
2. **Length Calculation**: The length of the vectors is calculated explicitly with `length(x)`.
3. **Memory Allocation**: An empty vector `output_vector <- c()` is initialized, and elements are filled in one by one in the loop.
4. **Error-prone**: If `x` and `y` have different lengths, this code will still run but may produce incorrect or unexpected results.
5. **Less Efficient**: Explicit loops in R are generally slower compared to vectorized operations.

### Code snippet 2

1. **Vectorized Operation**: This snippet uses R's built-in vectorized addition (`x + y`), which is more concise and generally more efficient.
2. **Implicit Length Handling**: The operation automatically handles the lengths of the vectors, without the need to explicitly calculate them.
3. **Memory Allocation**: The vector `output_vector` is created and filled in one line, making the code more concise.
4. **Less Error-prone**: If `x` and `y` have different lengths, R will still execute the code by recycling elements of the shorter vector to match the length of the longer one. While this could also lead to unexpected results, it is a feature of R's vectorized operations.

In summary, the second snippet is more idiomatic, concise, and efficient for this kind of simple element-wise operation in R.

2.19 Support independent replication

AI assistance tools could aid validation programmers or replicators in speeding up independent replication time. This is relevant in the pharma industry for clinical trial reporting, but could also be useful broadly in academia for internal or external replication of code. In the future, one could imagine a code validation LLM tool that could run in parallel or as a replacement to the validation programmers. This needs to be approached with care to ensure the production and validation programmer are not using an AI assistance tool for their work which may lead to errors being missed in the validation process.

2.20 Translate from language to language

Convert code from one language to another. Of course, here you want to ensure you have unit tests in place and the ability to understand both languages.

Convert the following R code into python using 
packages like pandas or numpy:
flights |> 
  mutate(
    gain = dep_delay - arr_delay,
    speed = distance / air_time * 60
  )
import pandas as pd

# Assuming you have a DataFrame called 'flights'
flights['gain'] = flights['dep_delay'] - flights['arr_delay']
flights['speed'] = flights['distance'] / flights['air_time'] * 60

2.21 Write documentation

Explain what kind of R package or analysis you are working on then ask the AI tool to create a README.md template for an R package. Additionally, you could use it to create in-line documentation as comments for your functions. This is commonly done in R using the roxygen2 package.

Give me a roxygen2 header for this R function to 
redact email addresses from a string: 

redact_email_addresses <- function(
text, 
replace_with = NULL) {
    pattern <-
      "\\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}(?:\\.[A-Za-z]{2,})?\\b"
    redact_pattern(text, pattern, replace_with)
}
#' Redact email addresses from a string.
#'
#' This function searches for email addresses in the input text and replaces them with the specified 'replace_with' value if provided, otherwise redacts them.
#'
#' @param text The input text containing email addresses to be redacted.
#' @param replace_with A character string used to replace the found email addresses. If set to NULL (default), the email addresses will be redacted.
#' @return The text with email addresses redacted or replaced.
#' @importFrom stringr str_replace_all
#' @export
#' @examples
#' redact_email_addresses("Contact us at john.doe@example.com or jane.smith@example.com", "[email]")
#' redact_email_addresses("Email support: support@example.com", "REDACTED")
#' redact_email_addresses("No emails here!")
#'
#' @seealso \code{\link[stringr]{str_replace_all}}
#' @keywords redact email addresses
redact_email_addresses <- function(
  text,
  replace_with = NULL
) {
  pattern <- "\\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}(?:\\.[A-Za-z]{2,})?\\b"
  redact_pattern(text, pattern, replace_with)
}

2.22 Write articles

Blog posts provide a useful medium to share projects and results. Additionally, vignettes serve as long form guides to using an package. Both are common for R and can serve as useful training materials to orient new users to custom software. With enough context and careful prompting, ChatGPT can help provide such types of articles. See this repo for an example of an R package generated primarily (including the README and vignettes) using ChatGPT.

2.23 Write a professional email

Provide an informal email or message to the AI tool and ask the tool to make it professional based on the intended audience or recipient.

Please update the following email to ensure it is business 
professional: 
`
Subject: Hey! Long Time No Talk!

Hey [Friend's Name],

How've you been? It feels like ages since we caught up. How's everything on your end?

Let's grab coffee sometime soon. I miss our chats!

Take care,
[Your Name]

Feel free to adapt it as you see fit!
`
Subject: Reconnecting to Discuss Ongoing Projects and Industry Developments

Dear [Colleague's Name],

I hope this email finds you well. It has been some time since we last connected, and I am eager to catch up on your recent accomplishments and the progress of your ongoing projects.

Would you be interested in scheduling a meeting to discuss industry trends and potential collaborative opportunities? A coffee catch-up would be an excellent way to facilitate this discussion.

Please let me know your availability for the coming weeks, and I would be happy to coordinate a meeting accordingly.

Thank you for your time, and I look forward to reconnecting soon.

Best regards,
[Your Name]
[Your Position]

2.24 Naming things

There are only two hard things in Computer Science: cache invalidation and naming things. – Phil Karlton

Well, AI tools can help you name things. For example, you could ask the AI tool to provide you could share the intended purpose for the function you’re working on then ask the AI tool for one or more informative function names.

Give me 5 possible names for an function that creates a 
hex sticker from a reference image. Use snake case 
for the names. Keep it at three words max.
create_hex_sticker
generate_hex_sticker
make_hex_sticker
build_hex_sticker
design_hex_sticker

2.26 Create custom GPTs

You can now create custom versions of ChatGPT They can be designed for specific tasks such as teaching math to your kids, explain board games, or understanding memes.

For example, you could create a GPT to aid you in Shiny for R app development called Shiny Guru. Here’s another custom example called Fit Coach to provide you coaching for strength and longevity between programming sessions. The process of creating GPTs is very easy and is done directly via chat.

2.27 Summary

In this chapter, we explored a bunch of ways to use AI tools to help you learn, create, and share. You learned that these tools can help you name things, write code, and even generate art. The key is to be creative and think outside the box. Combine ideas from this chapter or other materials to create your own unique workflows. Most importantly, be sure to read and verify the output from these tools. I hope you found this post useful and that it inspires you to try out some of these ideas.