diff --git a/L1Trigger/CSCTriggerPrimitives/README.md b/L1Trigger/CSCTriggerPrimitives/README.md
index deb3fa0dd44a5..bbad22d9da23a 100644
--- a/L1Trigger/CSCTriggerPrimitives/README.md
+++ b/L1Trigger/CSCTriggerPrimitives/README.md
@@ -25,7 +25,7 @@ The C++ code is located in `L1Trigger/CSCTriggerPrimitives/src/` and `L1Trigger/
 
 The `plugins/` directory contains the producer module `CSCTriggerPrimitivesProducer`. The `CSCTriggerPrimitivesReader` should be used for firmware vs emulator comparisons.
 
-The `src/` directory contains the builder `CSCTriggerPrimitivesBuilder`, processors (`CSCAnodeLCTProcessor`,  `CSCCathodeLCTProcessor`, `GEMCoPadProcessor`), motherboards (`CSCMotherboard` and similar names), a muon port card (`CSCMuonPortCard`), auxiliary classes to produce and access look-up-tables (`CSCUpgradeMotherboardLUT` and `CSCUpgradeMotherboardLUTGenerator`) and a module to fit a straight line to comparator digis (`CSCComparatorDigiFitter`). Trigger patterns are stored in `CSCPatternBank`.
+The `src/` directory contains the builder `CSCTriggerPrimitivesBuilder`, processors (`CSCAnodeLCTProcessor`,  `CSCCathodeLCTProcessor`, `GEMCoPadProcessor`), motherboards (`CSCMotherboard` and similar names), a muon port card (`CSCMuonPortCard`), auxiliary classes to produce and access look-up-tables (`CSCUpgradeMotherboardLUT` and `CSCUpgradeMotherboardLUTGenerator`). Trigger patterns are stored in `CSCPatternBank`.
 
 The `CSCTriggerPrimitivesBuilder` instantiates the TMBs for each chamber and the MPCs for each trigger sector. TMBs and MPC are organized in trigger sectors. A trigger sector has 9 associated TMBs and a single MPC. Once instantiated, the TMBs are configured and run according to settings defined `cscTriggerPrimitiveDigis_cfi` (see Configuration). After running the TMB the ALCT/CLCT/LCT collections are read out and put into the event. After all TMBs are run, the MPCs produce LCTs to be sent to the OMTF and EMTF.
 
@@ -37,7 +37,7 @@ The `CSCMuonPortCard` class collects LCTs from a trigger sector and relays them
 
 The `CSCUpgradeMotherboardLUTGenerator` and `CSCUpgradeMotherboardLUT` produce and contain look-up-tables that are used in the CSC upgrade algorithm and/or the GEM-CSC algorithm.
 
-The `CSCComparatorDigiFitter` is a relatively new feature in CSC trigger. It fits a straight line to a set of comparator digis associated to an LCT. The fitted LCT position has a resolution which is better by about a factor 2 than for non-fitted LCTs. This module will at some point be integrated into the `CSCCathodeLCTProcessor` to produce CLCTs with a better position resolution. Better LCT position is critical to reconstruct L1 muons for displaced signatures, one of the cornerstones of the Phase-2 muon upgrade.
+A new class is `CSCComparatorCodeLUT` which provides access to look-up-tables for improved local bending and position in Run-3 (CCLUT). Better LCT position and bending is critical to reconstruct L1 muons for displaced signatures, one of the cornerstones of the Phase-2 muon upgrade.
 
 The `test/` directory contains python configuration to test the CSC local trigger and analyzer the data.
 
@@ -96,3 +96,8 @@ Recent work includes:
 * The removal of outdated classes `CSCTriggerGeometry` and `CSCTriggerGeomManager` (https://github.com/cms-sw/cmssw/pull/21655)
 * The removal of a lot of outdated code (https://github.com/cms-sw/cmssw/pull/24171), which was used for very early MC studies (early 2000s) or to emulate beam-test data from the Magnet Test and Cosmic Challenge (MTCC - 2006). In case you need to look back at this code, please look at the [CMSSW_10_2_X branch](https://github.com/cms-sw/cmssw/tree/CMSSW_10_2_X/L1Trigger/CSCTriggerPrimitives).
 * The removal of an outdated DQM module (https://github.com/cms-sw/cmssw/pull/24196).
+* The removal of pre-2007 code (https://github.com/cms-sw/cmssw/pull/24254)
+* Implementation of a common baseboard class (https://github.com/cms-sw/cmssw/pull/24403)
+* Update GE1/1-ME1/1 and GE2/1-ME2/1 local triggers (https://github.com/cms-sw/cmssw/pull/27957, https://github.com/cms-sw/cmssw/pull/28334, https://github.com/cms-sw/cmssw/pull/28605)
+* Improvements to the CLCT algorithm following data vs emulator studies Summer and Fall of 2018 (https://github.com/cms-sw/cmssw/pull/25165)
+* Implementation of the CCLUT algorithm (https://github.com/cms-sw/cmssw/pull/28044, https://github.com/cms-sw/cmssw/pull/28600, https://github.com/cms-sw/cmssw/pull/29205, https://github.com/cms-sw/cmssw/pull/28846)
\ No newline at end of file
diff --git a/L1Trigger/CSCTriggerPrimitives/interface/CSCCathodeLCTProcessor.h b/L1Trigger/CSCTriggerPrimitives/interface/CSCCathodeLCTProcessor.h
index 0dba5cd5b0ee5..ae9be09f2878a 100644
--- a/L1Trigger/CSCTriggerPrimitives/interface/CSCCathodeLCTProcessor.h
+++ b/L1Trigger/CSCTriggerPrimitives/interface/CSCCathodeLCTProcessor.h
@@ -34,8 +34,11 @@
 #include "DataFormats/CSCDigi/interface/CSCCLCTDigi.h"
 #include "DataFormats/CSCDigi/interface/CSCCLCTPreTriggerDigi.h"
 #include "L1Trigger/CSCTriggerPrimitives/interface/CSCBaseboard.h"
+#include "L1Trigger/CSCTriggerPrimitives/interface/CSCComparatorCodeLUT.h"
 
 #include <vector>
+#include <array>
+#include <string>
 
 class CSCCathodeLCTProcessor : public CSCBaseboard {
 public:
@@ -88,6 +91,10 @@ class CSCCathodeLCTProcessor : public CSCBaseboard {
   /** LCTs in this chamber, as found by the processor. */
   CSCCLCTDigi CLCTContainer_[CSCConstants::MAX_CLCT_TBINS][CSCConstants::MAX_CLCTS_PER_PROCESSOR];
 
+  // unique pointers to the luts
+  std::array<std::unique_ptr<CSCComparatorCodeLUT>, 5> lutpos_;
+  std::array<std::unique_ptr<CSCComparatorCodeLUT>, 5> lutslope_;
+
   /** Access routines to comparator digis. */
   bool getDigis(const CSCComparatorDigiCollection* compdc);
   void getDigis(const CSCComparatorDigiCollection* compdc, const CSCDetId& id);
@@ -125,7 +132,7 @@ class CSCCathodeLCTProcessor : public CSCBaseboard {
   // enum used in the comparator code logic
   enum CLCT_CompCode { INVALID_HALFSTRIP = 65535 };
 
-  void cleanComparatorContainer(CSCCLCTDigi::ComparatorContainer& compHits) const;
+  void cleanComparatorContainer(CSCCLCTDigi& lct) const;
 
   /* Mark the half-strips around the best half-strip as busy */
   void markBusyKeys(const int best_hstrip, const int best_patid, int quality[CSCConstants::NUM_HALF_STRIPS_7CFEBS]);
@@ -138,6 +145,19 @@ class CSCCathodeLCTProcessor : public CSCBaseboard {
   void dumpDigis(const std::vector<int> strip[CSCConstants::NUM_LAYERS][CSCConstants::NUM_HALF_STRIPS_7CFEBS],
                  const int nStrips) const;
 
+  // --------Functions for the comparator code algorith for Run-3 ---------//
+  //calculates the id based on location of hits
+  int calculateComparatorCode(const std::array<std::array<int, 3>, 6>& halfStripPattern) const;
+
+  // sets the 1/4 and 1/8 strip bits given a floating point position offset
+  void calculatePositionCC(float offset, uint16_t& halfstrip, bool& quartstrip, bool& eightstrip) const;
+
+  // converts the floating point slope into integer slope
+  int calculateSlopeCC(float slope, int nBits) const;
+
+  // runs the CCLUT procedure
+  void runCCLUT(CSCCLCTDigi& digi) const;
+
   //--------------------------- Member variables -----------------------------
 
   /* best pattern Id for a given half-strip */
@@ -196,9 +216,8 @@ class CSCCathodeLCTProcessor : public CSCBaseboard {
   // Use the new patterns according to the comparator code format
   bool use_run3_patterns_;
   bool use_comparator_codes_;
-
-  // which hits per CLCT?
-  PulseArray hitsCLCT[99];
+  unsigned int nbits_position_cc_;
+  unsigned int nbits_slope_cc_;
 
   /** Default values of configuration parameters. */
   static const unsigned int def_fifo_tbins, def_fifo_pretrig;
@@ -207,6 +226,9 @@ class CSCCathodeLCTProcessor : public CSCBaseboard {
   static const unsigned int def_nplanes_hit_pattern;
   static const unsigned int def_pid_thresh_pretrig, def_min_separation;
   static const unsigned int def_tmb_l1a_window_size;
+
+  std::vector<std::string> positionLUTFiles_;
+  std::vector<std::string> slopeLUTFiles_;
 };
 
 #endif
diff --git a/L1Trigger/CSCTriggerPrimitives/interface/CSCComparatorCodeLUT.h b/L1Trigger/CSCTriggerPrimitives/interface/CSCComparatorCodeLUT.h
new file mode 100644
index 0000000000000..ec8faadaa0e14
--- /dev/null
+++ b/L1Trigger/CSCTriggerPrimitives/interface/CSCComparatorCodeLUT.h
@@ -0,0 +1,67 @@
+#ifndef L1Trigger_CSCTriggerPrimitives_CSCComparatorCodeLUT
+#define L1Trigger_CSCTriggerPrimitives_CSCComparatorCodeLUT
+
+#include <fstream>
+#include <sstream>
+#include <bitset>
+#include <iostream>
+#include <vector>
+#include <limits>
+
+#include "FWCore/Framework/interface/Frameworkfwd.h"
+#include "FWCore/ParameterSet/interface/ParameterSet.h"
+
+class CSCComparatorCodeLUT {
+public:
+  enum ReadCodes {
+    SUCCESS = 0,
+    NO_ENTRIES = 1,
+    DUP_ENTRIES = 2,
+    MISS_ENTRIES = 3,
+    MAX_ADDRESS_OUTOFRANGE = 4,
+    NO_HEADER = 5
+  };
+
+  /* CSCComparatorCodeLUT(); */
+  explicit CSCComparatorCodeLUT(const std::string&);
+  /* explicit CSCComparatorCodeLUT(l1t::LUT*); */
+  ~CSCComparatorCodeLUT() {}
+
+  float lookup(int code) const;
+  float lookupPacked(const int input) const;
+
+  // populates the map.
+  void initialize();
+
+  unsigned checkedInput(unsigned in, unsigned maxWidth) const;
+
+  // I/O functions
+  void save(std::ofstream& output);
+  int load(const std::string& inFileName);
+
+  float data(unsigned int address) const;
+  int read(std::istream& stream);
+  void write(std::ostream& stream) const;
+
+  unsigned int nrBitsAddress() const { return nrBitsAddress_; }
+  unsigned int nrBitsData() const { return nrBitsData_; }
+  //following the convention of vector::size()
+  unsigned int maxSize() const;
+  bool empty() const { return data_.empty(); }
+
+private:
+  int readHeader(std::istream&);
+
+  unsigned int nrBitsAddress_;  //technically redundant with addressMask
+  unsigned int nrBitsData_;     //technically redundant with dataMask
+  unsigned int addressMask_;
+  unsigned int dataMask_;
+
+  std::vector<float> data_;
+
+  int m_codeInWidth;
+  unsigned m_outWidth;
+  bool m_initialized;
+};
+
+#endif
diff --git a/L1Trigger/CSCTriggerPrimitives/interface/CSCMotherboard.h b/L1Trigger/CSCTriggerPrimitives/interface/CSCMotherboard.h
index 0c8011ff2774e..8e39d96d4b100 100644
--- a/L1Trigger/CSCTriggerPrimitives/interface/CSCMotherboard.h
+++ b/L1Trigger/CSCTriggerPrimitives/interface/CSCMotherboard.h
@@ -118,6 +118,9 @@ class CSCMotherboard : public CSCBaseboard {
   unsigned int highMultiplicityBits_;
   bool useHighMultiplicityBits_;
 
+  // Use the new patterns according to the comparator code format
+  bool use_run3_patterns_;
+
   /** Default values of configuration parameters. */
   static const unsigned int def_mpc_block_me1a;
   static const unsigned int def_alct_trig_enable, def_clct_trig_enable;
diff --git a/L1Trigger/CSCTriggerPrimitives/python/cscTriggerPrimitiveDigis_cfi.py b/L1Trigger/CSCTriggerPrimitives/python/cscTriggerPrimitiveDigis_cfi.py
index 3b421cb0b0da0..325f5f2c1b975 100644
--- a/L1Trigger/CSCTriggerPrimitives/python/cscTriggerPrimitiveDigis_cfi.py
+++ b/L1Trigger/CSCTriggerPrimitives/python/cscTriggerPrimitiveDigis_cfi.py
@@ -28,6 +28,22 @@
     # Write out pre-triggers
     savePreTriggers = cms.bool(False),
 
+    positionLUTFiles = cms.vstring(
+        "L1Trigger/CSCTriggerPrimitives/data/CSCComparatorCodePosOffsetLUT_pat0_ideal_v1.txt",
+        "L1Trigger/CSCTriggerPrimitives/data/CSCComparatorCodePosOffsetLUT_pat1_ideal_v1.txt",
+        "L1Trigger/CSCTriggerPrimitives/data/CSCComparatorCodePosOffsetLUT_pat2_ideal_v1.txt",
+        "L1Trigger/CSCTriggerPrimitives/data/CSCComparatorCodePosOffsetLUT_pat3_ideal_v1.txt",
+        "L1Trigger/CSCTriggerPrimitives/data/CSCComparatorCodePosOffsetLUT_pat4_ideal_v1.txt"
+    ),
+
+    slopeLUTFiles = cms.vstring(
+        "L1Trigger/CSCTriggerPrimitives/data/CSCComparatorCodeSlopeLUT_pat0_v1.txt",
+        "L1Trigger/CSCTriggerPrimitives/data/CSCComparatorCodeSlopeLUT_pat1_v1.txt",
+        "L1Trigger/CSCTriggerPrimitives/data/CSCComparatorCodeSlopeLUT_pat2_v1.txt",
+        "L1Trigger/CSCTriggerPrimitives/data/CSCComparatorCodeSlopeLUT_pat3_v1.txt",
+        "L1Trigger/CSCTriggerPrimitives/data/CSCComparatorCodeSlopeLUT_pat4_v1.txt"
+    ),
+
     # Parameters common for all boards
     commonParam = cms.PSet(
         # Master flag for SLHC studies
@@ -165,6 +181,8 @@
         useRun3Patterns = cms.bool(False),
 
         useComparatorCodes = cms.bool(False),
+        nBitsPositionCC = cms.uint32(10),
+        nBitsSlopeCC = cms.uint32(5)
     ),
 
     # Parameters for CLCT processors: SLHC studies
@@ -211,6 +229,9 @@
         useRun3Patterns = cms.bool(False),
 
         useComparatorCodes = cms.bool(False),
+
+        nBitsPositionCC = cms.uint32(10),
+        nBitsSlopeCC = cms.uint32(5)
     ),
 
     tmbParam = cms.PSet(
diff --git a/L1Trigger/CSCTriggerPrimitives/src/CSCCathodeLCTProcessor.cc b/L1Trigger/CSCTriggerPrimitives/src/CSCCathodeLCTProcessor.cc
index 6e2c7c854fa27..39438f38ff842 100644
--- a/L1Trigger/CSCTriggerPrimitives/src/CSCCathodeLCTProcessor.cc
+++ b/L1Trigger/CSCTriggerPrimitives/src/CSCCathodeLCTProcessor.cc
@@ -61,6 +61,10 @@ CSCCathodeLCTProcessor::CSCCathodeLCTProcessor(unsigned endcap,
 
   use_comparator_codes_ = clctParams_.getParameter<bool>("useComparatorCodes");
 
+  nbits_position_cc_ = clctParams_.getParameter<unsigned int>("nBitsPositionCC");
+
+  nbits_slope_cc_ = clctParams_.getParameter<unsigned int>("nBitsSlopeCC");
+
   // Check and print configuration parameters.
   checkConfigParameters();
   if ((infoV > 0) && !config_dumped) {
@@ -84,6 +88,16 @@ CSCCathodeLCTProcessor::CSCCathodeLCTProcessor(unsigned endcap,
     clct_pattern_ = CSCPatternBank::clct_pattern_legacy_;
   }
 
+  if (use_comparator_codes_) {
+    positionLUTFiles_ = conf.getParameter<std::vector<std::string>>("positionLUTFiles");
+    slopeLUTFiles_ = conf.getParameter<std::vector<std::string>>("slopeLUTFiles");
+
+    for (int i = 0; i < 5; ++i) {
+      lutpos_[i].reset(new CSCComparatorCodeLUT(positionLUTFiles_[i]));
+      lutslope_[i].reset(new CSCComparatorCodeLUT(slopeLUTFiles_[i]));
+    }
+  }
+
   thePreTriggerDigis.clear();
 }
 
@@ -661,19 +675,23 @@ std::vector<CSCCLCTDigi> CSCCathodeLCTProcessor::findLCTs(
                                 keystrip_data[ilct][CLCT_BX]);
 
             // get the comparator hits for this pattern
-            auto compHits = hits_in_patterns[best_hs][keystrip_data[ilct][CLCT_PATTERN]];
-
-            // purge the comparator digi collection from the obsolete "65535" entries...
-            cleanComparatorContainer(compHits);
+            const auto& compHits = hits_in_patterns[best_hs][keystrip_data[ilct][CLCT_PATTERN]];
 
             // set the hit collection
             thisLCT.setHits(compHits);
 
+            // do the CCLUT procedures
+            if (use_comparator_codes_) {
+              runCCLUT(thisLCT);
+            }
+
+            // purge the comparator digi collection from the obsolete "65535" entries...
+            cleanComparatorContainer(thisLCT);
+
             // useful debugging
             if (infoV > 1) {
               LogTrace("CSCCathodeLCTProcessor") << " Final selection: ilct " << ilct << " " << thisLCT << std::endl;
             }
-
             // put the CLCT into the collection
             lctList.push_back(thisLCT);
           }
@@ -863,7 +881,7 @@ bool CSCCathodeLCTProcessor::patternFinding(
   // Loop over candidate key strips.
   for (int key_hstrip = stagger[CSCConstants::KEY_CLCT_LAYER - 1]; key_hstrip < nStrips; key_hstrip++) {
     // Loop over patterns and look for hits matching each pattern.
-    for (unsigned int pid = clct_pattern_.size() - 1; pid >= pid_thresh_pretrig; pid--) {
+    for (unsigned int pid = clct_pattern_.size() - 1; pid >= pid_thresh_pretrig and pid < clct_pattern_.size(); pid--) {
       layers_hit = 0;
       // clear all layers
       for (int ilayer = 0; ilayer < CSCConstants::NUM_LAYERS; ilayer++) {
@@ -989,12 +1007,14 @@ void CSCCathodeLCTProcessor::markBusyKeys(const int best_hstrip,
   }
 }  // markBusyKeys -- TMB-07 version.
 
-void CSCCathodeLCTProcessor::cleanComparatorContainer(CSCCLCTDigi::ComparatorContainer& compHits) const {
-  for (auto& p : compHits) {
+void CSCCathodeLCTProcessor::cleanComparatorContainer(CSCCLCTDigi& clct) const {
+  CSCCLCTDigi::ComparatorContainer newHits = clct.getHits();
+  for (auto& p : newHits) {
     p.erase(std::remove_if(
                 p.begin(), p.end(), [](unsigned i) -> bool { return i == CSCCathodeLCTProcessor::INVALID_HALFSTRIP; }),
             p.end());
   }
+  clct.setHits(newHits);
 }
 
 // --------------------------------------------------------------------------
@@ -1226,3 +1246,191 @@ CSCCLCTDigi CSCCathodeLCTProcessor::getSecondCLCT(int bx) const {
   lct.setBX(lct.getBX() + alctClctOffset_);
   return lct;
 }
+
+// --------Functions for the comparator code algorith for Run-3 ---------//
+
+int CSCCathodeLCTProcessor::calculateComparatorCode(const std::array<std::array<int, 3>, 6>& halfStripPattern) const {
+  int id = 0;
+
+  for (unsigned int column = 0; column < CSCConstants::NUM_LAYERS; column++) {
+    int rowPat = 0;   //physical arrangement of the three bits
+    int rowCode = 0;  //code used to identify the arrangement
+
+    //use Firmware definition for comparator code definition
+    for (int row = 2; row >= 0; row--) {
+      rowPat = rowPat << 1;  //bitshift the last number to the left
+      rowPat += halfStripPattern[column][row];
+    }
+    switch (rowPat) {
+      case 0:  //000
+        rowCode = 0;
+        break;
+      case 1:  //00X
+        rowCode = 1;
+        break;
+      case 2:  //0X0
+        rowCode = 2;
+        break;
+      case 4:  //00X
+        rowCode = 3;
+        break;
+      default:
+        // default return value is -1
+        return -1;
+    }
+    //each column has two bits of information, largest layer is most significant bit
+    id += (rowCode << 2 * column);
+  }
+  return id;
+}
+
+void CSCCathodeLCTProcessor::calculatePositionCC(float offset,
+                                                 uint16_t& halfstrip,
+                                                 bool& quartstrip,
+                                                 bool& eightstrip) const {
+  // offset is too small, no bits are are set!
+  if (std::abs(offset) < 0.25)
+    return;
+
+  // if the offset is less than -0.25, reduce the halfstrip number by 1
+  float positiveOffset = offset;
+  if (offset <= -0.25 and halfstrip >= 1) {
+    halfstrip = halfstrip - 1;
+    positiveOffset = positiveOffset + 1;
+    // offset by one more halfstrip!
+    if (offset <= -1.25) {
+      halfstrip = halfstrip - 1;
+      positiveOffset = positiveOffset + 1;
+    }
+  }
+
+  // if the offset is more than 1, increase halfstrip by 1
+  if (offset >= 1 and halfstrip <= numStrips) {
+    halfstrip = halfstrip + 1;
+    positiveOffset = positiveOffset - 1;
+    // offset by one more halfstrip!
+    if (offset >= 2) {
+      halfstrip = halfstrip + 1;
+      positiveOffset = positiveOffset - 1;
+    }
+  }
+
+  // determine the quart and eight strip bits
+  if (0 < positiveOffset and positiveOffset < 0.5) {
+    quartstrip = false;
+    if (positiveOffset < 0.25)
+      eightstrip = false;
+    if (positiveOffset > 0.25)
+      eightstrip = true;
+  }
+  if (0.5 < positiveOffset and positiveOffset < 1) {
+    quartstrip = true;
+    if (positiveOffset < 0.75)
+      eightstrip = false;
+    if (positiveOffset > 0.75)
+      eightstrip = true;
+  }
+}
+
+int CSCCathodeLCTProcessor::calculateSlopeCC(float slope, int nBits) const {
+  int returnValue;
+  float minSlope = -1.0;
+  float maxSlope = 1.0;
+  int range = pow(2, nBits);
+  float deltaSlope = (maxSlope - minSlope) / range;
+
+  if (slope <= -1.0)
+    returnValue = 0;
+  else if (slope >= 1.0)
+    returnValue = range - 1;
+  else if (slope == 0) {
+    returnValue = pow(2, nBits - 1);
+  } else {
+    if (slope < 0) {
+      returnValue = std::floor(std::abs(slope) / deltaSlope);
+    } else {
+      returnValue = std::floor(std::abs(slope) / deltaSlope) + pow(2, nBits - 1);
+    }
+  }
+  return returnValue;
+}
+
+void CSCCathodeLCTProcessor::runCCLUT(CSCCLCTDigi& digi) const {
+  // print out the old CLCT for debugging
+  if (infoV > 2) {
+    std::ostringstream strm;
+    strm << "\n";
+    strm << "++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n";
+    strm << "+                  Before CCCLUT algorithm:                       +\n";
+    strm << "++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n";
+    strm << " Old CLCT digi " << digi << "\n";
+    strm << " 1/4 strip bit " << digi.getQuartStrip() << " 1/8 strip bit " << digi.getEightStrip() << "\n";
+    strm << " 1/4 strip number " << digi.getKeyStrip(4) << " 1/8 strip number " << digi.getKeyStrip(8) << "\n";
+    strm << "++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n";
+    LogDebug("CSCCathodeLCTProcessor") << strm.str();
+  }
+
+  // Get the comparator hits
+  auto compHits = digi.getHits();
+
+  // Wrap the comparator code in a format for calculation
+  std::array<std::array<int, 3>, CSCConstants::NUM_LAYERS> compHitsCC;
+
+  for (int i = 0; i < CSCConstants::NUM_LAYERS; i++) {
+    int iCC = 0;
+    for (int j = 0; j < CSCConstants::CLCT_PATTERN_WIDTH; j++) {
+      // only fill when the pattern is active
+      if (clct_pattern_[digi.getPattern()][i][j]) {
+        if (compHits[i][j] != INVALID_HALFSTRIP) {
+          compHitsCC[i][iCC] = 1;
+        } else {
+          compHitsCC[i][iCC] = 0;
+        }
+        iCC++;
+      }
+    }
+  }
+
+  // calculate the comparator code
+  int comparatorCode = calculateComparatorCode(compHitsCC);
+
+  // store the comparator code
+  digi.setCompCode(comparatorCode);
+
+  // calculate the slope and position offset
+  int pattern = digi.getPattern();
+  // position offset is in strips -> *2 to get to half-strips!
+  float positionCC = 2 * lutpos_[pattern]->lookup(comparatorCode);
+  float slopeCC = lutslope_[pattern]->lookup(comparatorCode);
+
+  // calculate the new position
+  uint16_t halfstrip = digi.getStrip();
+  bool quartstrip;
+  bool eightstrip;
+  calculatePositionCC(positionCC, halfstrip, quartstrip, eightstrip);
+
+  // store the new 1/2, 1/4 and 1/8 strip positions
+  digi.setStrip(halfstrip);
+  digi.setQuartStrip(quartstrip);
+  digi.setEightStrip(eightstrip);
+
+  // store the bending angle value
+  digi.setBend(calculateSlopeCC(slopeCC, nbits_slope_cc_));
+
+  // set Run-3 flag
+  digi.setRun3(true);
+
+  // now print out the new CLCT for debugging
+  if (infoV > 2) {
+    std::ostringstream strm;
+    strm << "\n";
+    strm << "++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n";
+    strm << "+                  CCCLUT algorithm results:                       +\n";
+    strm << "++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n";
+    strm << " New CLCT digi " << digi << "\n";
+    strm << " 1/4 strip bit " << digi.getQuartStrip() << " 1/8 strip bit " << digi.getEightStrip() << "\n";
+    strm << " 1/4 strip number " << digi.getKeyStrip(4) << " 1/8 strip number " << digi.getKeyStrip(8) << "\n";
+    strm << "++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n";
+    LogDebug("CSCCathodeLCTProcessor") << strm.str();
+  }
+}
diff --git a/L1Trigger/CSCTriggerPrimitives/src/CSCComparatorCodeLUT.cc b/L1Trigger/CSCTriggerPrimitives/src/CSCComparatorCodeLUT.cc
new file mode 100644
index 0000000000000..89c453ee8542e
--- /dev/null
+++ b/L1Trigger/CSCTriggerPrimitives/src/CSCComparatorCodeLUT.cc
@@ -0,0 +1,159 @@
+#include "L1Trigger/CSCTriggerPrimitives/interface/CSCComparatorCodeLUT.h"
+#include "FWCore/Utilities/interface/Exception.h"
+#include "FWCore/MessageLogger/interface/MessageLogger.h"
+
+CSCComparatorCodeLUT::CSCComparatorCodeLUT(const std::string& fname)
+    : nrBitsAddress_(0), nrBitsData_(0), addressMask_(0), dataMask_(0), data_(), m_codeInWidth(12), m_outWidth(32) {
+  if (fname != std::string("")) {
+    load(fname);
+  } else {
+    initialize();
+  }
+}
+
+// I/O functions
+void CSCComparatorCodeLUT::save(std::ofstream& output) { write(output); }
+
+float CSCComparatorCodeLUT::data(unsigned int address) const {
+  return (address & addressMask_) < data_.size() ? data_[address] : 0;
+}
+
+int CSCComparatorCodeLUT::load(const std::string& inFileName) {
+  std::ifstream fstream;
+  fstream.open(edm::FileInPath(inFileName.c_str()).fullPath());
+  if (!fstream.good()) {
+    fstream.close();
+    throw cms::Exception("FileOpenError") << "Failed to open LUT file: " << inFileName;
+  }
+  int readCode = read(fstream);
+
+  m_initialized = true;
+  fstream.close();
+
+  return readCode;
+}
+
+float CSCComparatorCodeLUT::lookup(int code) const {
+  if (m_initialized) {
+    return lookupPacked(code);
+  }
+  return 0;
+}
+
+float CSCComparatorCodeLUT::lookupPacked(const int input) const {
+  if (m_initialized) {
+    return data((unsigned int)input);
+  }
+  throw cms::Exception("Uninitialized") << "If you're not loading a LUT from file you need to implement lookupPacked.";
+  return 0;
+}
+
+void CSCComparatorCodeLUT::initialize() {
+  if (empty()) {
+    std::stringstream stream;
+    stream << "#<header> V1 " << m_codeInWidth << " " << m_outWidth << " </header> " << std::endl;
+    for (int in = 0; in < (1 << m_codeInWidth); ++in) {
+      int out = lookup(in);
+      stream << in << " " << out << std::endl;
+    }
+    read(stream);
+  }
+  m_initialized = true;
+}
+
+unsigned CSCComparatorCodeLUT::checkedInput(unsigned in, unsigned maxWidth) const {
+  unsigned maxIn = (1 << maxWidth) - 1;
+  return (in < maxIn ? in : maxIn);
+}
+
+int CSCComparatorCodeLUT::read(std::istream& stream) {
+  data_.clear();
+
+  int readHeaderCode = readHeader(stream);
+  if (readHeaderCode != SUCCESS)
+    return readHeaderCode;
+
+  std::vector<std::pair<unsigned int, float> > entries;
+  unsigned int maxAddress = addressMask_;
+  std::string line;
+
+  while (std::getline(stream, line)) {
+    line.erase(std::find(line.begin(), line.end(), '#'), line.end());  //ignore comments
+    std::istringstream lineStream(line);
+    std::pair<unsigned int, float> entry;
+    while (lineStream >> entry.first >> entry.second) {
+      entry.first &= addressMask_;
+      // entry.second &= dataMask_;
+      entries.push_back(entry);
+      if (entry.first > maxAddress || maxAddress == addressMask_)
+        maxAddress = entry.first;
+    }
+  }
+  std::sort(entries.begin(), entries.end());
+  if (entries.empty()) {
+    //log the error we read nothing
+    return NO_ENTRIES;
+  }
+  //this check is redundant as dups are also picked up by the next check but might make for easier debugging
+  if (std::adjacent_find(entries.begin(), entries.end(), [](auto const& a, auto const& b) {
+        return a.first == b.first;
+      }) != entries.end()) {
+    //log the error that we have duplicate addresses once masked
+    return DUP_ENTRIES;
+  }
+  if (entries.front().first != 0 ||
+      std::adjacent_find(entries.begin(), entries.end(), [](auto const& a, auto const& b) {
+        return a.first + 1 != b.first;
+      }) != entries.end()) {
+    //log the error that we have a missing entry
+    return MISS_ENTRIES;
+  }
+
+  if (maxAddress != std::numeric_limits<unsigned int>::max())
+    data_.resize(maxAddress + 1, 0);
+  else {
+    //log the error that we have more addresses than we can deal with (which is 4gb so something probably has gone wrong anyways)
+    return MAX_ADDRESS_OUTOFRANGE;
+  }
+
+  std::transform(entries.begin(), entries.end(), data_.begin(), [](auto const& x) { return x.second; });
+  return SUCCESS;
+}
+
+void CSCComparatorCodeLUT::write(std::ostream& stream) const {
+  stream << "#<header> V1 " << nrBitsAddress_ << " " << nrBitsData_ << " </header> " << std::endl;
+  for (unsigned int address = 0; address < data_.size(); address++) {
+    stream << (address & addressMask_) << " " << data(address) << std::endl;
+  }
+}
+
+unsigned int CSCComparatorCodeLUT::maxSize() const {
+  return addressMask_ == std::numeric_limits<unsigned int>::max() ? addressMask_ : addressMask_ + 1;
+}
+
+int CSCComparatorCodeLUT::readHeader(std::istream& stream) {
+  int startPos = stream.tellg();  //we are going to reset to this position before we exit
+  std::string line;
+  while (std::getline(stream, line)) {
+    if (line.find("#<header>") == 0) {  //line
+      std::istringstream lineStream(line);
+
+      std::string version;      //currently not doing anything with this
+      std::string headerField;  //currently not doing anything with this
+      if (lineStream >> headerField >> version >> nrBitsAddress_ >> nrBitsData_) {
+        addressMask_ = nrBitsAddress_ != 32 ? (0x1 << nrBitsAddress_) - 1 : ~0x0;
+        dataMask_ = (0x1 << nrBitsData_) - 1;
+        stream.seekg(startPos);
+        return SUCCESS;
+      }
+    }
+  }
+
+  nrBitsAddress_ = 0;
+  nrBitsData_ = 0;
+  addressMask_ = (0x1 << nrBitsAddress_) - 1;
+  dataMask_ = (0x1 << nrBitsData_) - 1;
+
+  stream.seekg(startPos);
+  return NO_HEADER;
+}
diff --git a/L1Trigger/CSCTriggerPrimitives/src/CSCGEMMotherboard.cc b/L1Trigger/CSCTriggerPrimitives/src/CSCGEMMotherboard.cc
index ea5c3cea83def..8d9133834a2dd 100644
--- a/L1Trigger/CSCTriggerPrimitives/src/CSCGEMMotherboard.cc
+++ b/L1Trigger/CSCTriggerPrimitives/src/CSCGEMMotherboard.cc
@@ -189,10 +189,6 @@ CSCCorrelatedLCTDigi CSCGEMMotherboard::constructLCTsGEM(const CSCALCTDigi& alct
                                    << " detid " << cscId_ << " with wiregroup " << keyWG << "keyStrip " << keyStrip
                                    << " \n";
 
-  // in Run-3 we plan to use the synchronization error bit
-  // to denote the presence of exotic signatures in the chamber
-  unsigned int syncErr = useHighMultiplicityBits_ ? highMultiplicityBits_ : 0;
-
   // fill the rest of the properties
   thisLCT.setTrknmb(trknmb);
   thisLCT.setValid(valid);
@@ -204,8 +200,13 @@ CSCCorrelatedLCTDigi CSCGEMMotherboard::constructLCTsGEM(const CSCALCTDigi& alct
   thisLCT.setBX(bx);
   thisLCT.setMPCLink(0);
   thisLCT.setBX0(0);
-  thisLCT.setSyncErr(syncErr);
+  // Not used in Run-2. Will not be assigned in Run-3
+  thisLCT.setSyncErr(0);
   thisLCT.setCSCID(theTrigChamber);
+  thisLCT.setRun3(true);
+  // in Run-3 we plan to denote the presence of exotic signatures in the chamber
+  if (useHighMultiplicityBits_)
+    thisLCT.setHMT(highMultiplicityBits_);
 
   // future work: add a section that produces LCTs according
   // to the new LCT dataformat (not yet defined)
diff --git a/L1Trigger/CSCTriggerPrimitives/src/CSCMotherboard.cc b/L1Trigger/CSCTriggerPrimitives/src/CSCMotherboard.cc
index 0cf847a627286..4bef861084d90 100644
--- a/L1Trigger/CSCTriggerPrimitives/src/CSCMotherboard.cc
+++ b/L1Trigger/CSCTriggerPrimitives/src/CSCMotherboard.cc
@@ -478,7 +478,7 @@ CSCCorrelatedLCTDigi CSCMotherboard::constructLCTs(const CSCALCTDigi& aLCT,
                                                    int type,
                                                    int trknmb) const {
   // CLCT pattern number
-  unsigned int pattern = encodePattern(cLCT.getPattern());
+  unsigned int pattern = use_run3_patterns_ ? 0 : encodePattern(cLCT.getPattern());
 
   // LCT quality number
   unsigned int quality = findQuality(aLCT, cLCT);
@@ -486,9 +486,8 @@ CSCCorrelatedLCTDigi CSCMotherboard::constructLCTs(const CSCALCTDigi& aLCT,
   // Bunch crossing: get it from cathode LCT if anode LCT is not there.
   int bx = aLCT.isValid() ? aLCT.getBX() : cLCT.getBX();
 
-  // in Run-3 we plan to use the synchronization error bit
-  // to denote the presence of exotic signatures in the chamber
-  unsigned int syncErr = useHighMultiplicityBits_ ? highMultiplicityBits_ : 0;
+  // Not used in Run-2. Will not be assigned in Run-3
+  unsigned int syncErr = 0;
 
   // construct correlated LCT
   CSCCorrelatedLCTDigi thisLCT(trknmb,
@@ -504,6 +503,14 @@ CSCCorrelatedLCTDigi CSCMotherboard::constructLCTs(const CSCALCTDigi& aLCT,
                                syncErr,
                                theTrigChamber);
   thisLCT.setType(type);
+
+  if (use_run3_patterns_) {
+    thisLCT.setRun3(true);
+    // in Run-3 we plan to denote the presence of exotic signatures in the chamber
+    if (useHighMultiplicityBits_)
+      thisLCT.setHMT(highMultiplicityBits_);
+  }
+
   // make sure to shift the ALCT BX from 8 to 3 and the CLCT BX from 8 to 7!
   thisLCT.setALCT(getBXShiftedALCT(aLCT));
   thisLCT.setCLCT(getBXShiftedCLCT(cLCT));
diff --git a/L1Trigger/CSCTriggerPrimitives/src/CSCUpgradeCathodeLCTProcessor.cc b/L1Trigger/CSCTriggerPrimitives/src/CSCUpgradeCathodeLCTProcessor.cc
index 4a8a7af457973..e62765e41f24c 100644
--- a/L1Trigger/CSCTriggerPrimitives/src/CSCUpgradeCathodeLCTProcessor.cc
+++ b/L1Trigger/CSCTriggerPrimitives/src/CSCUpgradeCathodeLCTProcessor.cc
@@ -329,14 +329,19 @@ std::vector<CSCCLCTDigi> CSCUpgradeCathodeLCTProcessor::findLCTs(
             thisLCT.setFullBX(fbx);
 
             // get the comparator hits for this pattern
-            auto compHits = hits_in_patterns[best_hs][keystrip_data[ilct][CLCT_PATTERN]];
-
-            // purge the comparator digi collection from the obsolete "65535" entries...
-            cleanComparatorContainer(compHits);
+            const auto& compHits = hits_in_patterns[best_hs][keystrip_data[ilct][CLCT_PATTERN]];
 
             // set the hit collection
             thisLCT.setHits(compHits);
 
+            // do the CCLUT procedures
+            if (use_comparator_codes_) {
+              runCCLUT(thisLCT);
+            }
+
+            // purge the comparator digi collection from the obsolete "65535" entries...
+            cleanComparatorContainer(thisLCT);
+
             // put the CLCT into the collection
             lctList.push_back(thisLCT);
             lctListBX.push_back(thisLCT);