  # -------------------------------------------------------------------------------------------------------------------------  #
  #  -----------------------------------------------------------------------------------------------------------------------   #
  #                                                                                                                            #
  #           File Name              :  dict functions.r                                                                       #
  #           Last Updated Funclist  :  19 Feb 2015, 12:52 PM (Thursday)                                                       #
  #                                                                                                                            #
  #           Author Name            :  Rick Saporta                                                                           #
  #           Author Email           :  RickSaporta@gmail.com                                                                  #
  #           Author URL             :  www.github.com/rsaporta                                                                #
  #                                                                                                                            #
  #           Packages Called        :  NA                                                                                     #
  #           Packages Used via NS   :  base                                                                                   #
  #                                                                                                                            #
  #  -----------------------------------------------------------------------------------------------------------------------   #
  #                                                                                                                            #
  #   makeFactorUsingDict.quick ( vec, dict.with.names )                                                                       #
  #   makeFactorUsingDict       ( vec, dict.with.names, missing_to_NA=TRUE )                                                   #
  #   makeDictFromCSV           ( csvFile )                                                                                    #
  #   makeDictWithIntegerKeys   ( KVraw, applyLabels=TRUE )                                                                    #
  #   showDicts                 (  )                                                                                           #
  #   getDict                   ( dict.name, justnames=FALSE )                                                                 #
  #   existsl                   ( x, envir=e.this, ... )                                                                       #
  #   is.dict                   ( x )                                                                                          #
  #   print.dict                ( x, nrow=35, quote=FALSE )                                                                    #
  #   invDict                   ( dict )                                                                                       #
  #   setNamesDict              ( DT, dict, replaceMissing=NULL, silent=FALSE, showWarnings=!silent )                          #
  #                                                                                                                            #
  #                                                                                                                            #
  #                                                     <END FUNCS>                                                            #
  #  -----------------------------------------------------------------------------------------------------------------------   #
  # -------------------------------------------------------------------------------------------------------------------------  #

##  NOTE TO SELF
##  More dictionaries here: 
##     ~/git/orch/src/SpotifyAdds/supportFiles/dicts and column_info.r

replace_using_dict <- function(x, dict_with_x_in_names, preserve_names=TRUE) {
## Takes values in x, and if they appear in the *names* of dict, then those values are replaced with the corresponding values in dict
## values in x that do not match to any name in dict are left as is

  ret <- ifelse(is.na(dict_with_x_in_names[x]), x, dict_with_x_in_names[x])
  if (preserve_names)
    names(ret) <- names(x)
  else
    ret <- unname(ret)

  return(ret)
}

makeFactorUsingDict.quick <- function(vec, dict.with.names) {
## This is "quick" in that it does not perform any checks. Useful in by/apply situations
##  
## names(dict) are the labels (what vec will be ultimately)
## values of dict are the levels (what vec currently is)
  
  factor(vec, levels=dict.with.names, labels=names(dict.with.names)) 
}

makeFactorUsingDict <- function(vec, dict.with.names, missing_to_NA=TRUE) {
## names(dict) are the labels (what vec will be ultimately)
## values of dict are the levels (what vec currently is)
## 
## missing_to_NA:  if TRUE, any elements in vec that are not in names(dict.with.names) are set to NA
##                 if FALSE, those elements are left as is

  if (!is.character(vec))
    vec <- as.character(vec)

  if (is.character(dict.with.names) && !exists(dict.with.names))
    dict.with.names <- getDict(dict.with.names)

  if (!missing_to_NA) {
    valsToAdd <- setdiff(vec, names(dict.with.names))
    dict.with.names <- c(dict.with.names, setNames(nm=valsToAdd))
  }

  # browser()
  labs <- dict.with.names
  labs <- unique(labs)
  labs <- labs[!is.na(labs)]

  factor(dict.with.names[vec], levels=labs)
}

makeDictFromCSV <- function(csvFile)  {
  # Creates a dictionary out of a CSV file where 
  #    col1 of the CSV are the keys and col2 are the values.
  #
  # Arg:
  #   dictCSVPath: A path to a CSV file
  #
  # Returns a dictionary (list) s|t  dict["key"] = "value"
  #   eg: dict["LooonngWooord"] = "shortwrd" 
   
  c <- read.csv(csvFile)
  dict <- list(as.character(c[[2]]))
  names(dict[[1]]) <-(as.character(c[[1]]))
  rm(c) # keep it clean
  
  return(dict[[1]])
}



makeDictWithIntegerKeys <- function(KVraw, applyLabels=TRUE)  { 
    ###  problem: if 
    # we want a dict such that dict[aritstid] = source_name
    # PROBLEM:  since sourceid's are integers, dict[sourceid] will return the sourceid'th (nth) item 
    # eg:  dict[510] will return the 510th item of dict, not the source whose id is 510  *rather, not necessarily..  
    #      that is,  dict[510] != dict["510"]
    # 
    # this wouldnt be a problem if we can ensure that each sourceid gets loaded 
    # into dict at the position of its integer value
    # then dict[sourceid] and dict[sQuote(sourceid)] will return the same value
    #
    # Args: KVraw should be two-dim matrix with col1==Keys, and col2==Values, 
    #       applyLabels: if T, dict will have names st dict["123"] == dict[123]; 
    #                    if F, dict["123"] is undefined
    #                    NOTE: The labels are needed in order to be able to make calls like 
    #                          which(names(dict) %in% subsetOfKeys) where subsetOfKeys
    #                          is some collection of keys and we want the corresponding values
    # Return:
    #   a one-dim list where dict[key] == value, where key is an integer


    ## initialize the dict
    largestK <- max(KVraw[[1]])  # make sure we create enough room in dict
    dict <- rep(NA,largestK)     # note that length(dict) >= length(KVraw)
    names <- dict

    ## assign values
    for (i in 1:nrow(KVraw) )  {
      dict[as.integer(KVraw[[1]][i])] <- KVraw[[2]][i]
      names[as.integer(KVraw[[1]][i])] <- as.character(KVraw[[1]][i])
    }

    ## assign labels if option'd
    if (applyLabels) {
      names(dict) <- names      
    }

    return(dict)
}
  
showDicts <- function() {
  getDict(justnames=TRUE)
}

getDict <- function(dict.name, justnames=FALSE, fail_if_missing=TRUE, showWarnings=TRUE) {
## A simple wrapper function for storing several dictionary vectors

    if (missing(dict.name) && missing(justnames))
      justnames <- TRUE

    e.this <- environment()
    ## exists local, in the current environment
    existsl <- function(x, envir=e.this, ...) {
      exists(x, envir=envir, ..., inherits=FALSE)
    }

    # For label_sc_group  -- Sample usage:  P + color_by_dict("dict.colors.sc")
    # dict.colors.sc <- c(Orchard="#EB7028", RED="#af0d25", OSC="#3896eb", Allegro="#d250eb", SelectO="#1beba3", Spotify="#007A40")
    # dict.colors.sc <- c(Orchard="#EC9E3C", RED="#FD6677", OSC="#44AAEB", Allegro="#47AEF3", SelectO="#A879FF", Selecto="#A879FF", Spotify="#007A40")
    dict.colors.sc <- c(Orchard="#FFB87E", RED="#FD6677", OSC="#44AAEB", Allegro="#47AEF3", SelectO="#A879FF", Selecto="#A879FF", Spotify="#25D293", 'Spotify x-Orch'="#17D183")
    attr(dict.colors.sc, "title") <- "Supply Chain Group"
    # setattr(dict.colors.sc, "title", "Supply Chain Group")

    dict.colors.trans_abbr = c(DR="#7FC97F", DV="#BEAED4", VR="#FDC086", DT="#FFFF99", DA="#386CB0", VS="#F0027F", NR="#BF5B17", UT="#666666", UA='#D9D9D9', XX='#BC80BD') ## UA & XX might be somewhat repetitive
    attr(dict.colors.trans_abbr, "title") <- "Transac Type"
    # setattr(dict.colors.trans_abbr, "title", "Transac Type")

    dict.colors.orchard_vs_spotify = c(Orchard="#FFB87E", Spotify="#25D293", 'Spotify x-Orch'="#17D183")
    attr(dict.colors.trans_abbr, "title") <- "Orchard vs Spotify"
    # setattr(dict.colors.trans_abbr, "title", "Transac Type")

    ## Created using: 
    ##     RColorBrewer::brewer.pal(12, "Paired")[c((1:6) * 2, (1:6) * 2 - 1) ]

    ## Abbr Name:  Jan, .... 
    dict.colors.mnth <- setNames(
        nm  = month.abb
      , obj = c("#1F78B4", "#33A02C", "#E31A1C", "#FF7F00", "#6A3D9A", "#B15928", "#A6CEE3", "#B2DF8A", "#FB9A99", "#FDBF6F", "#CAB2D6", "#FFFF99")
    )
    ## Full Name:  January, .... 
    dict.colors.month <- setNames(
        nm  = month.name
      , obj = dict.colors.mnth
    )
    attr(dict.colors.mnth, "title") <- "Month"
    attr(dict.colors.month, "title") <- "Month"


    dict.colors.weekdays <- setNames (
        nm = c("Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday")
      , obj = c("#8DD3C7", "#FFFFB3", "#BEBADA", "#FB8072", "#80B1D3", "#FDB462", "#005824")
    )
    dict.colors.wdays <- setNames (
        nm = c("Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat")
      , obj = c("#8DD3C7", "#FFFFB3", "#BEBADA", "#FB8072", "#80B1D3", "#FDB462", "#005824")
    )
    attr(dict.colors.weekdays, "title") <- "Day of the Week"
    attr(dict.colors.wdays,    "title") <- "Day of the Week"

    dict.trans_short <- c(
        download = "DL"
      , Download = "DL"
      , Subscription = "Subcr"
      , Revenue = "Rev"
      , Ringtone = "Ringt"
      , Ringback = "Ringbk"
      , Upgraded = "Upgr"
      # , Album = "Alb"
      # , Track = "Trk"
      , Video = "Vid"
      , supported = "sup"
      , Supported = "Sup"
      , Stream = "Strm"
      , Mobile = "Mobl"
      , interactive = "intrctv"
      , Interactive = "Intrctv"
      , Unmonetized = "Unmontz"
    )
    attr(dict.trans_short, "title") <- "Transac Type"

    ## For converting column names
    dict.spotifymgmt.revshare <- c(
      #    ORIGINAL COL NAME  = NEW COL NAME
                       month  = "activity_month"
      ,        activity_date  = "activity_month"
      ,        gross_revenue  = "spotify_gross_revenue_cur"
      ,          net_revenue  = "spotify_net_revenue_cur"
      ,              payable  = "orchard_gross_revenue_cur"
      ,          payable_usd  = "orchard_gross_revenue_usd"
      ,          payable_eur  = "orchard_gross_revenue_eur"
      ,  rightholders_tracks  = "orchard_streams"
      ,         total_tracks  = "spotify_streams"
    )

    dict.NA.unparsed <- c(logical="NA", integer="NA_integer_", numeric="NA_real_", character="NA_character_")

    ## used in pasteQ
    dict.parens <- c("(" = ")", "[" = "]", "{" = "}", "<" = ">", "\"" = "\"", "\'" = "\'", "'%" = "%'")


    dict.convert_to_seconds <-  c(
                        "picoseconds"  = 1e-12
                      , "pico"     = 1e-12
                      , "nanoseconds"  = 1e-9
                      , "nano"     = 1e-9
                      , "microseconds" = 1e-6
                      , "micro"    = 1e-6
                      , "milliseconds" = 1e-3
                      , "milli"    = 1e-3
                      , "seconds"  = 1
                      , "secs"     = 1
                      , "minutes"  = 60
                      , "mins"     = 60
                      , "hours"    = 60*60
                      , "days"     = 60*60*24
                      , "year"     = 60*60*24 * 365.25
                      , "century"  = 60*60*24 * 365.25 * 100
                      )

    dict.dollar_scales <- c(
        #        KEY  = VALUE
                ones  = 10^00
        ,   hundreds  = 10^02
        ,  thousands  = 10^03
        ,   millions  = 10^06
        ,   billions  = 10^09
        ,  trillions  = 10^12
      )

    dict.numbs <- c(
        "one" = 1
      , "two" = 2
      , "three" = 3
      , "four" = 4
      , "five" = 5
      , "six" = 6
      , "seven" = 7
      , "eigth" = 8
      , "nine" = 9
      , "ten" = 10

      , "14" = 14
      , "28" = 28
      , "29" = 29
      , "30" = 30
      )

    dict.url <- c(
      #  RAW  = SAFE
         "%"  = "%25" ## make sure "%" is first
      ,  "!"  = "%21"
      ,  "@"  = "%40"
      ,  "#"  = "%23"
      ,  "$"  = "%24"
      ,  "¢"  = "%C2%A2"
      ,  "£" = "%C2%A3"
      ,  "^"  = "%5E"
      ,  "&"  = "%26"
      ,  "*"  = "%2A"
      ,  "("  = "%28"
      ,  ")"  = "%29"
      ,  "["  = "%5b"
      ,  "]"  = "%5d"
      ,  "+"  = "%2B"
      ,  "="  = "%3D"
      ,  "?"  = "%3F"
      ,  "/"  = "%2F"
      ,  ","  = "%2C"
      ,  " "  = "+"
      ,  "\t"  = "%09"
      ,  ";" = "%3B"
      ,  ":" = "%3A"
      ,  "~" = "%7E"
      ,  "`" = "%60"
      ,  "\'" = "%27"
      ,  "\"" = "%22"
      ,  "ä" = "%C3%A4"
      ,  "ø" = "%C3%B8"
      ,  "ñ" = "%C3%B1"
      ,  "Ç" = "%C3%87"
      # ,  "-"  = "-"
      # ,  "_"  = "_"
      # ,  "."  = "."
     )

    dict.url_large <- c(
      #  RAW  = SAFE
         "%"  = "%25" ## make sure "%" is first
      ,  "!"  = "%21"
      ,  "@"  = "%40"
      ,  "#"  = "%23"
      ,  "$"  = "%24"
      ,  "¢"  = "%C2%A2"
      ,  "£" = "%C2%A3"
      ,  "^"  = "%5E"
      ,  "&"  = "%26"
      ,  "*"  = "%2A"
      ,  "("  = "%28"
      ,  ")"  = "%29"
      ,  "["  = "%5b"
      ,  "]"  = "%5d"
      ,  "+"  = "%2B"
      ,  "="  = "%3D"
      ,  "?"  = "%3F"
      ,  "/"  = "%2F"
      ,  ","  = "%2C"
      ,  " "  = "+"
      ,  "\t"  = "%09"
      ,  ";" = "%3B"
      ,  ":" = "%3A"
      ,  "~" = "%7E"
      ,  "`" = "%60"
      ,  "\'" = "%27"
      ,  "\"" = "%22"
      ,  "ä" = "%C3%A4"
      ,  "ø" = "%C3%B8"
      ,  "ñ" = "%C3%B1"
      , "¥" = "%C2%A5"
      , "¿" = "%C2%BF"
      , "À" = "%C3%80"
      , "Á" = "%C3%81"
      , "Â" = "%C3%82"
      , "Ã" = "%C3%83"
      , "Ä" = "%C3%84"
      , "Å" = "%C3%85"
      , "Æ" = "%C3%86"
      , "Ç" = "%C3%87"
      , "È" = "%C3%88"
      , "É" = "%C3%89"
      , "Ë" = "%C3%8B"
      , "Ì" = "%C3%8C"
      , "Í" = "%C3%8D"
      , "Î" = "%C3%8E"
      , "Ï" = "%C3%8F"
      , "Ð" = "%C3%90"
      , "Ñ" = "%C3%91"
      , "Ò" = "%C3%92"
      , "Ó" = "%C3%93"
      , "Ô" = "%C3%94"
      , "Õ" = "%C3%95"
      , "Ö" = "%C3%96"
      , "×" = "%C3%97"
      , "Ø" = "%C3%98"
      , "Ù" = "%C3%99"
      , "Ú" = "%C3%9A"
      , "Û" = "%C3%9B"
      , "Ü" = "%C3%9C"
      , "Ý" = "%C3%9D"
      , "Þ" = "%C3%9E"
      , "ß" = "%C3%9F"
      , "à" = "%C3%A0"
      , "á" = "%C3%A1"
      , "â" = "%C3%A2"
      , "ã" = "%C3%A3"
      , "ä" = "%C3%A4"
      , "å" = "%C3%A5"
      , "æ" = "%C3%A6"
      , "ç" = "%C3%A7"
      , "è" = "%C3%A8"
      , "é" = "%C3%A9"
      , "ê" = "%C3%AA"
      , "ë" = "%C3%AB"
      , "ì" = "%C3%AC"
      , "í" = "%C3%AD"
      , "î" = "%C3%AE"
      , "ï" = "%C3%AF"
      , "ð" = "%C3%B0"
      , "ñ" = "%C3%B1"
      , "ò" = "%C3%B2"
      , "ó" = "%C3%B3"
      , "ô" = "%C3%B4"
      , "õ" = "%C3%B5"
      , "ö" = "%C3%B6"
      , "ø" = "%C3%B8"
      , "ù" = "%C3%B9"
      , "ú" = "%C3%BA"
      , "û" = "%C3%BB"
      , "ü" = "%C3%BC"
      , "ý" = "%C3%BD"
      , "ÿ" = "%C3%BF"
      # ,  "-"  = "-"
      # ,  "_"  = "_"
      # ,  "."  = "."
     )


    dict.colors.genre <- c(
        #                 KEY  = VALUE
                "Alternative"  = "springgreen"
        ,           "Country"  = "indianred4" # "maroon4"
        ,        "Electronic"  = "mediumseagreen"
        ,       "Hip-Hop/Rap"  = "darkorange"
        ,             "Metal"  = "dodgerblue2"   # wheet1
        ,               "Pop"  = "turquoise3"
        ,              "Punk"  = "orchid" # "wheat1" # "darkred"
        ,          "R&B/Soul"  = "darkorchid"
        ,              "Rock"  = "firebrick2"
        #              COMPRESSED VALUES
        ,  "Electronic/Dance"  = "mediumseagreen"
        ,     "Rock/Punk/Alt"  = "springgreen"
        ,           "Rap/R&B"  = "darkorange"
        ,             "OTHER"  = "deepskyblue2"
        ,             "Other"  = "deepskyblue2"
        ,                "ZZ"  = "grey15"
        ,         "Childrens"  = "firebrick2"
        ,             "Latin"  = "wheat4"
      )


    dict.bi.accounting <- c(
      #              ALIAS (original) =  FIELD NAME (new)
                     "partner_share"  =  "partner_commission"
      ,                      "sales"  =  "units"
      ,       "original_currency_id"  =  "original_currency_id"
      ,               "retail_price"  =  "retail_price"
      ,           "activity_fx_rate"  =  "currency_exchange_rate_at_inbound"
      ,              "fx_spread_fee"  =  "unknown_fx_spread_usd"
      ,                      "gross"  =  "gross_revenue_usd"
      ,                   "oms_fees"  =  "fees_mechanical_admin_usd"
      ,             "adjusted_gross"  =  "adjusted_gross_revenue_usd"
      ,          "distribution_fees"  =  "fees_distribution_usd"
      ,                "net_receipt"  =  "client_net_receipt_usd"
      ,             "dpd_publishing"  =  "fees_dpd_publishing_usd"
      ,        "ringtone_publishing"  =  "fees_ringtone_publishing_usd"
      ,                 "actual_net"  =  "client_actual_net_usd"
      ,         "payout_currency_id"  =  "payout_currency_id"
      ,  "fx_adjusted_exchange_rate"  =  "currency_exchange_rate_at_payout"
      ,                   "fx_gross"  =  "gross_revenue_ccur"
      ,                "fx_oms_fees"  =  "fees_mechanical_admin_ccur"
      ,          "fx_adjusted_gross"  =  "adjusted_gross_ccur"
      ,       "fx_distribution_fees"  =  "fees_distribution_ccur"
      ,             "fx_net_receipt"  =  "client_net_receipt_ccur"
      ,          "fx_dpd_publishing"  =  "fees_dpd_publishing_ccur"
      ,     "fx_ringtone_publishing"  =  "fees_ringtone_publishing_ccur"
      ,              "fx_actual_net"  =  "client_actual_net_ccur"
    )   

    ## NOTE:
    if (FALSE) {
      intToUtf8(65281:65374)
      intToUtf8(33:126)
      "！＂＃＄％＆＇（）＊＋，－．／０１２３４５６７８９：；＜＝＞？＠ＡＢＣＤＥＦＧＨＩＪＫＬＭＮＯＰＱＲＳＴＵＶＷＸＹＺ［＼］＾＿｀ａｂｃｄｅｆｇｈｉｊｋｌｍｎｏｐｑｒｓｔｕｖｗｘｙｚ｛｜｝～"
      "!\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~"
    }
    dict.letter_cleanup =
    c(
      "àáâäæãåāèéëêēęeîïíīįìôöòóœøōõûüùúūñńçćčÿśšłžźżÀÁÂÄÆÃÅĀÈÉËÊĒĘEÎÏÍĪĮÌÔÖÒÓŒØŌÕÛÜÙÚŪÑŃÇĆČŸŚŠŁŽŹŻ！-～"
        =
      "aaaaaaaaeeeeeeeiiiiiioooooooouuuuunncccysslzzzAAAAAAAAEEEEEEEIIIIIIOOOOOOOOUUUUUNNCCCYSSLZZZ!-~"
    )

  ## ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ END OF DICTS ... FUNCTION CONTINUES ~~~~~~~~~~~~~~ ##



  if (justnames) {
    ret <- sort(ls(pattern="^dict"))
    print(cbind("  " = ret), quote=FALSE)
    return(invisible(ret))
  }

  if (!is.character(dict.name) || length(dict.name) > 1)
    stop ("'dict.name' should be a character of length 1.")
  if (dict.name == "Orchard_vs_Spotify")
    dict.name <- "orchard_vs_spotify"
  if (grepl("[A-Z]", dict.name)) {
    warning ("dict.name in getDict() should be all lowercase", call.=FALSE)
    dict.name %<>% tolower()
  }

  ## 20141203 -- changed all color dictionaries to have the word "colors" first
  ##             eg  from  dict.month.colors  to  dict.colors.month
  ## For backwards compatability, check the input of dict.name
  if (grepl("\\.colors$", dict.name)) {
    warning("the color-dictionary names have been changed to the format\n   dict.colors.______\nPlease update your code")
    if (grepl("^dict\\.(.*)", dict.name))
      dict.name <-  gsub("dict\\.", "dict.colors.", gsub("\\.colors$", "", dict.name))
    else 
      dict.name <- paste0("colors.", gsub("\\.colors$", "", dict.name))
  }

  ## Allow for the name to include or not include the "dict." prefix
  if (!existsl(dict.name) && existsl(paste0("dict.", dict.name)))
    dict.name <- paste0("dict.", dict.name)
  if (!existsl(dict.name) && existsl(paste0("dict.colors.", dict.name)))
    dict.name <- paste0("dict.colors.", dict.name)
  if (!existsl(dict.name) && existsl(gsub("dict.", "dict.colors.", dict.name)))
    dict.name <- gsub("dict.", "dict.colors.", dict.name)

  ## If still cannot find the dict, throw an error
  if (!existsl(dict.name))  {
    msg <- sprintf("Cannot find dictionary '%s'", dict.name)
    if (fail_if_missing)
      stop(msg, "\n")
    if (showWarnings)
      warning(msg)
    return(NULL)
  }
  
  ret <- get(dict.name)

  ## set the class attributes, ideally by reference
  if (exists("setattr"))
    classAppend_(ret, "dict")
  else
    class(ret) <- unique(c("dict", class(ret)))
  
  return(ret)
}

is.dict <- function(x) {
  inherits(x, "dict")
}

as.dict <- function(...) {
  classAppend_(c(...), "dict")
}

print.dict <- function(x, nrow=35, quote=FALSE, maxdots=35, key.nm="KEY", value.nm="VALUE") {
  if (!length(x)) {
    warning ("x has no keys or vals")
    return(base::print.default(x))
  }
  if (is.null(names(x))) {
    warning ("x has no names (ie, no keys)")
    return(base::print.default(x))
  }

  keys <- c(names(x), sprintf("[ %s ]", key.nm))
  vals <- c(sapply(x, as.character, USE.NAMES=FALSE), sprintf("[ %s ]", value.nm))

  n.k <- nchar(keys)
  n.v <- nchar(vals)

  ## keep it trim
  trim_perc <- (pmax(1, (n.k + n.v) / maxdots))
  n.k <- floor(n.k / trim_perc)
  n.v <- floor(n.v / trim_perc)

  if (quote) {
    keys <- sprintf("\"%s\"", keys)
    vals <- sprintf("\"%s\"", vals)
  }

  keys.spaced <- paste(keys, pasteR(".", max(n.k) - n.k + 1))
  vals.spaced <- paste(pasteR(".", min(maxdots, max(n.v)) - n.v + 1), vals)

  nc <- nchar(paste0(keys.spaced, vals.spaced))
  ret <- cbind(paste0(keys.spaced, pasteR(".", 3), vals.spaced))
  ## Any line that is too long, remove the dots
  mdn <- median(nchar(ret))
  safetybreak <- 120
  while (any(toowide <- nchar(ret) > mdn & grepl("\\.{4,}", ret)) && safetybreak) {
    # browser(expr=nchar(ret)[[19]] < 50, text = "SDFdsfdsf")
    safetybreak <- safetybreak - 1
    ret[toowide] <- sub("\\.\\.", ".", ret[toowide])    
  }

  out <- capture.output(print(ret, quote=quote)) [ -1]
  ## Bring the last line forward, removing the [dd, ]  prefix
  l.out <- length(out)
  pat.rem <- sprintf("\\[%s,\\]", l.out)
  repl <- pasteR(" ", nchar(pat.rem) - 2 - (length(x)>=10))
  # out <- c(sub(pat.rem, repl, out[l.out]), out[-l.out])
  header <- sub(pat.rem, repl, out[l.out])

  cat(header, "\n")
  width.bak <- getOption("width")
  options(width=max(10, min(nc)))
  print(ret[-l.out], quote=FALSE)
  options(width=width.bak)  

  return(invisible(ret))
}

invDict <- function(dict)  {
## inverts a dictionary (ie, swapping the names with the values)
  nms <- names(dict)
  if (is.null(nms))
    nms <- dict
  setNames(obj=nms, nm=dict)
}

setNamesDict <- function(..., deprecated="Use setnamesByDict()") {
  setnamesByDict(...)
}

setnamesByDict <- function(DT, dict, replaceMissing=NULL, silent=FALSE, noNeedToCheckInv=FALSE, showWarnings=!silent, warn_for_new_return=TRUE) {
#  Replaces names of DT with values of `dict` where ever there is a match
#     between `names(dict)` and `names(DT)`
#  dict should be  "oldColumnName" = "newColumnName"
#  If replaceMissing is specified, column names of DT which are not present
#     in dict will be replaced with the value of replaceMissing

  if (warn_for_new_return) {
    warning("As of Jan 2016 changed return value\n\nPreviously setnamesByDict returned TRUE/FALSE to indicate success.\nNow it returns the DT, to allow for daisy chaining\n\nuse  warn_for_new_return=FALSE to turn this message off\n\n")
    Sys.sleep(2)
  }

  nm <- copy(names(DT))

  # check if dict needs to be inverted
  if (!noNeedToCheckInv) {
    if(sum(dict %in% nm) > sum(names(dict) %in% nm)) {
        dict <- setNames(names(dict), dict) 
          verboseMsg(showWarnings, "inverting dictionary")
    }
  }

  # check if there are no matching values
  matched <- nm %in% names(dict)
  if (!any(matched)) {
        verboseMsg(showWarnings, "No values in the dict match the column names of the data.table")
      return(invisible(DT))
  }

  # Only partial matches... 
  if (!all(matched)) {
    # optionally warn: 
      verboseMsg(showWarnings, "Not all names in the DT are being replaced (ie names(DT) were not in the dict): ", length(nm) - sum(!matched), " missing", ifelse(missing(replaceMissing), ".", " and being replaced."))

    # optionally replace missing values
    if (!missing(replaceMissing))
      setnames(DT, nm[!matched], paste(replaceMissing, 1:sum(!matched), sep="."))
  }

  setnames(DT, nm[matched], dict[nm[matched]])

  return(invisible(DT))
}
